User-Restricted Access to Data

In some cases, you don’t want your users to search your whole set of records. You want them to search only the subset that is specifically tailored to them, like all the movies they bookmarked, or all the items they added to their shopping list, or all the content shared by their friends.

Dataset

Because Algolia is schemaless and does not have any concept of the relationship between objects, you’ll need to put all of the relevant information in each record.

Let’s use a blog post dataset as an example, where each post should be visible only to some users.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[
  {
    "objectID": "myID1",
    "name": "Mobile Search UX",
    "author_name": "Bob",
    "viewable_by": [1, 2, 3]
  },
  {
    "objectID": "myID2",
    "name": "Keeping mobile apps lightweight",
    "author_name": "Henry",
    "viewable_by": [3]
  },
  {
    "objectID": "myID3",
    "name": "Comparing Algolia and Elasticsearch",
    "author_name": "Doug",
    "viewable_by": [1, 2]
  }
]

As you can see, each record contains a viewable_by array containing a list of user IDs: only users listed in this array will be able to view the post.

You can download the dataset here. Have look at how to import it in Algolia here.

Initialize Client

1
2
3
4
5
6
7
8
9
10
11
12
// composer autoload
require __DIR__ . '/vendor/autoload.php';

// if you are not using composer
// require_once 'path/to/algoliasearch.php';

$client = \Algolia\AlgoliaSearch\SearchClient::create(
  'YourApplicationID',
  'YourAdminAPIKey'
);

$index = $client->initIndex('your_index_name');

Making viewable_by filterable

To make the attribute filterable we will add it in attributesForFaceting.

1
2
3
4
5
$index->setSettings([
  'attributesForFaceting' => [
    'filterOnly(viewable_by)'
  ]
]);

Because in this example we are only going to filter on this attribute, we added the filterOnly modifier. This is better from a performance point of view, because Algolia won’t have to compute the count for each value. If you need faceting on this attribute, just remove the filterOnly modifier.

Updating the viewable_by array

Whenever someone bookmarks/unbookmarks a blog post, you’ll have to update the viewable_by array to add/remove the corresponding user_id.

1
2
3
4
5
6
$index->partialUpdateObject(
    [
        'viewable_by' => [1, 2],
        'objectID' => 'myID1'
    ]
);

In the previous snippet we replaced the content of the attribute completely using the partial update method. This partial update method also allows you to update an attribute in different ways, like adding/removing a element to an array.

Generating a Secured API Key (in the backend)

If your search is completely done in the front-end, malicious users could tweak the request and get access to content they are not allowed to see. To prevent this, we are going generate a Secured API Key, which is a special API key with a set of filters. Those filters are pre-integrated inside the key and therefore cannot be changed by the user.

1
2
3
4
5
6
7
8
$currentUserID = 1; // Replace by the current user ID

$securedApiKey = \AlgoliaSearch\Client::generateSecuredApiKey(
  'YourSearchOnlyApiKey', // Make sure to use a search key
  [
    'filters' => 'viewable_by:'.$currentUserID
  ]
);

For every user we’ll use this code to generate a key containing the right filter.

To invalidate the generated Secured API Keys, you’ll need to invalidate the search API key they have been generated from.

Searching in the subset (in the frontend)

1
2
3
4
5
6
7
8
9
var securedApiKey = "Secured API Key for current user"; // Use the key generated earlier

var client = algoliasearch("YourApplicationID", securedApiKey);
var index = client.initIndex('your_index_name');

// only query string
index.search('query string', function searchDone(err, content) {
  console.log(content.hits);
});

The user ID filter being inside the key, we do a normal search and it will return only records which have the current user ID inside its viewable_by attribute.

Did you find this page helpful?