Concepts / Building Search UI / API keys/security
Aug. 06, 2019

API Keys/security

You are reading the documentation for Vue InstantSearch v2. Read our migration guide to learn how to upgrade from v1 to v2. You can still find the v1 documentation here.

Search-Only API Keys on web

Inside your application you can use the search-only API Key. You can include this key directly in your front-end code. It might happen that you need to apply some rate limiting to your API Key. For those cases, you can generate what we call Secured API Keys.

Secured API Keys

Secured API Keys are useful when you want to restrict the search to a set of indices, for example. Those keys need to be generated on the back-end. Otherwise, users can modify the restrictions of the key by modifying the front-end code. There is already a tutorial about how to use Secured API Keys, but this guide will focus on the usage of those keys inside a Vue InstantSearch application. We will implement an application that generates a secured API Key on the server, then uses it on the client. The secured API Key will have a restriction on the index. You can find the complete example on GitHub.

Server

Generate the Secured API Key

The first step is to generate our Secured API Key on the server. In this example we are using Express but the concepts could be applied to any server. To generate our key we need the Algolia JavaScript client and a “parent key” that has the search ACL. Most of the time this “parent key” will be the search-only API Key. In this example we restrict our key to the index demo_ecommerce. It means that with this key the front end won’t be able to target a different index. You can apply different kind of restrictions and also apply a set of search parameters.

1
2
3
4
5
6
7
8
9
const algoliasearch = require('algoliasearch');

const client = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
const securedApiKey = client.generateSecuredApiKey(
  'YourSearchOnlyAPIKey',
  {
    restrictIndices: 'demo_ecommerce',
  }
);

Once we have the key we need to pass it down to our client. We have the choice between two different implementations:

  • create an endpoint on your server that will send back the API Key. Then on the client before mounting the application you need to asynchronously fetch the API key from the server.
  • inline the API key in the HTML. Then on the client we can directly mount the application by reading the value from the global object.

In this guide we will go for the second option: inline the API Key.

Inline the API Key

For simplicity, in this example we use Vue CLI. But with Vue CLI we don’t create the HTML file from scratch, we use the one generated by the CLI. To inject data from the server we can use a placeholder value (this link is for Create React App, but also applies in the case of Vue CLI) in the HTML. Note that this step is only useful if you are using Vue CLI. Otherwise you can directly inline the value inside your HTML template.

1
2
3
4
5
6
7
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <script>
      window.SERVER_DATA = __SERVER_DATA__;
    </script>

Once the placeholder is setup we can replace it with the generated API Key.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const path = require('path');
const fs = require('fs');
const util = require('util');
const express = require('express');

const app = express();
const readFileAsync = util.promisify(fs.readFile);

app.get('/', async (_, res) => {
  const index = await readFileAsync(
    path.join(__dirname, 'dist', 'index.html'),
    'utf-8'
  );

  const indexWithServerData = index.replace(
    '__SERVER_DATA__',
    JSON.stringify({
      ALGOLIA_API_KEY: securedApiKey,
    })
  );

  res.send(indexWithServerData);
});

That’s it for the server! You should now be able to run the server and access your generated API Key from the client with window.SERVER_DATA. Take a look at the next section to learn how to use this key in the Vue InstantSearch application.

Client

Retrieve the API Key

Now that we have the API Key on the global object we can retrieve it from our client code and inject it into our searchClient. Don’t forget to clean up the global object otherwise this value will stay in memory.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Vue from 'vue';
import App from './App.vue';
import InstantSearch from 'vue-instantsearch';

Vue.use(InstantSearch);

// eslint-disable-next-line no-new
new Vue({
  el: '#app',
  render: h =>
    h(App, {
      props: {
        apiKey: window.SERVER_DATA.ALGOLIA_API_KEY,
      },
    }),
});

Use the injected searchClient

The last part is to use the injected searchClient in our React application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
  <ais-instant-search
    :search-client="searchClient"
    index-name="demo_ecommerce"
  >
    <!-- InstantSearch widgets -->
  </ais-instant-search>
</template>

<script>
import algoliasearch from 'algoliasearch/lite';
import 'instantsearch.css/themes/algolia-min.css';

export default {
  props: {
    apiKey: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      searchClient: algoliasearch('YourApplicationID', this.apiKey),
    };
  },
};
</script>

That’s it for the client! Your application can now only target the index demo_ecommerce. You can try to target a different one like demo_media but the API will return an error. Don’t forget to take a look at the complete example on GitHub.

XSS - Preventing attacks from within user generated content.

Algolia handles highlighting within the engine. By leveraging this feature, you give your user a way to know how their query matches the results, it is a very important cue.

Technically this means that the engine will surround the matching words with tags. By default, we use simple HTML tags like <em></em> and we let the browser render the content as HTML. This leaves a potential security hole, especially in the context of user generated content.

The same problem exists for the snippeting feature.

To fix this, Vue InstantSearch will force the use of a known set of tags and will escape all the other HTML tags. This means that if you’ve set a custom tag in the Algolia Dashboard it will be overridden and replaced by mark. If you want to use another one, like for example strong, you need to specify the highlight-tag-name props in ais-highlight or ais-snippet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
  <ais-instant-search
    index-name="instant_search"
    :search-client="searchClient"
  >
    <ais-hits>
      <template slot="item" slot-scope="{ item }">
        <p>
          <ais-highlight attribute="name" :hit="item" />
          <ais-snippet attribute="description" :hit="item" />
        </p>
      </template>
    </ais-hits>
  </ais-instant-search>
</template>

To disable this safety feature all together, you can use :escape-hits="false" on the ais-hits component, and then the HTML will no longer be escaped.

Did you find this page helpful?