Search Token Authentication

Search tokens are special JSON web tokens that can be used to execute queries and send usage analytics events as a specific user. They are intended to be used in JavaScript code running in a web browser, typically along with the Coveo JavaScript Search Framework.

By default, search tokens automatically expire after 24 hours (see the validFor property).

You can generate search tokens in server-side code by calling a REST service exposed through the Coveo Cloud platform (see Requesting a Search Token).

Typically, you will want to use search token authentication when your search page users are authenticated and some (or all) items in your index are secured. In this scenario, each end user gets a unique search token, allowing the search interface to securely return only items that the user is allowed to see (see Sample Usage Workflow).

Sample Usage Workflow

Here is a typical workflow demonstrating the use of search tokens:

  1. A user requests a Coveo search interface from a web server.

  2. The web server executes server-side code that eventually renders the HTML response (PHP, ASP.NET, etc.).

  3. Server-side code authenticates the user who is making the request.

  4. Server-side code calls a REST service exposed through the Coveo Cloud platform to get a search token for the authenticated user (see Requesting a Search Token).

  5. The search token is used to generate the JavaScript code that initializes the Coveo search interface (see Configuring a New Search Endpoint).

  6. The server sends the generated HTML to the client.

  7. The JavaScript code initializes the search interface and executes the first query, using the provided search token.

  8. The Coveo Cloud platform executes the query as the user impersonated by the search token.

  9. Results are displayed to the user.

Node.js Example

You could implement a Node.js Express middleware function and application to serve a JavaScript Search Framework interface configured with a valid search token.

A middleware function that requests a search token for the authenticated user

// ./middlewares.js
 
"use strict"
const request = require("request");
 
module.exports = {
  getSearchToken: (req, res, next) => {
 
    // Replace `MY_USERS`, `MY_SEARCH_HUB`, and `MY_FILTER_EXPRESSION`.
    const postData = {
      userIds: MY_USERS.map(user => {
        return { name: user, provider: "Email Security Provider" }
      }),
      searchHub: "MY_SEARCH_HUB",
      filter: "MY_FILTER_EXPRESSION"
    };
 
    request(
      "https://platform.cloud.coveo.com/rest/search/v2/token",
      {
        // Replace `MY_API_KEY`.
        auth: { bearer: "MY_API_KEY" },
        json: true,
        method: "POST",
        body: postData
      },
      (error, response, body) => {
        if (error) {
          next(error);
        } else if (response.statusCode != 200) {
          next(JSON.stringify(res, null, 2));
        } else {
          req.token = body.token;
          next();
        }
      }
    );
  }
}

An application that calls the above middleware function when the / route is requested, and passes the generated search token to the template to render

// ./index.js
 
"use strict"
const express = require("express");
const app = express();
const middlewares = require("./middlewares.js");
 
app.set("view engine", "ejs");
 
app.get("/", middlewares.getSearchToken, (req, res) => {
  res.render("index", {
    token: req.token
  });
});
 
app.listen(3000);

The search endpoint must then be configured with the token before serving the page.

A basic template for a search page

<!-- ./views/index.ejs -->
 
<!DOCTYPE html>
<html lang="en">
 
<head>
  <script class="coveo-script" src="https://static.cloud.coveo.com/searchui/v2.7968/js/CoveoJsSearch.Lazy.min.js"></script>
  <link rel="stylesheet" href="https://static.cloud.coveo.com/searchui/v2.7968/css/CoveoFullSearch.css" />
  <script src="https://static.cloud.coveo.com/searchui/v2.7968/js/templates/templates.js"></script>
  <script>
    document.addEventListener("DOMContentLoaded", () => {
      Coveo.SearchEndpoint.configureCloudV2Endpoint(
        "mycoveocloudorganization",
        "<%- token %>"
      );
      Coveo.init(document.getElementById("search"));
    })
  </script>
</head>
 
<body id="search" class="CoveoSearchInterface">
  <!-- ... -->
  <div class="CoveoAnalytics"></div>
  <div class="coveo-search-section">
    <div class="CoveoSearchbox"></div>
  </div>
  <div class="coveo-main-section">
    </div class="CoveoResultList"></div>
  </div>
  <!-- ... -->
</body>
 
</html>

Or, if the requested page includes a Coveo In-Product Experience (IPX) interface, the token must be injected into the loader script URL, as the value of the search_token query parameter.

A basic template for a page that includes an IPX interface

<!-- ./views/index.ejs -->
 
<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
 
  <!--  Coveo In-Product Experience -->
  <script type="text/javascript" src="https://platform.cloud.coveo.com/rest/organizations/mycoveocloudorganization/pages/abc8ccfe-bf50-42e7-a140-475420cbc543/inappwidget/loader?search_token=<%- token %>" async ></script>
  <!-- End Coveo In-Product Experience -->
 
  <!-- ... -->
</head>
 
<body>
  <!-- ... -->
</body>
 
</html>

Requesting a Search Token

Make a POST request to https://platform.cloud.coveo.com/rest/search/token (or https://cloudplatform.coveo.com/rest/search/token on Coveo Cloud V1) to request a search token. On Coveo Cloud, you can use Swagger UI to test the service (see Create search token).

The caller must authenticate using an API key with the privilege to impersonate users (see Adding and Managing API Keys).

Never expose an API key with the Allowed access level on the Impersonate domain in client-side code. Always request search tokens in secure, server-side code (see Privilege Management and Impersonate Domain).

Sample Request

A search token creation call in which you only specify the required values

POST https://platform.cloud.coveo.com/rest/search/token HTTP/1.1
 
Content-Type: application/json
Accept: application/json
Authorization: Bearer **********-****-****-****-************

Payload

{
  "userIds": [
    {
      "name": "asmith@example.com",
      "provider": "Email Security Provider"
    }
  ]
}

Request Body Properties

The body of a POST request to the /rest/search/v2/token route has the following properties:

userIds (array of RestUserId, required)

The security identities to impersonate when authenticating a query with this search token.

Properties:

name (string, required)

The name of the security identity to impersonate.

This value can also be used in query pipeline condition statements (e.g., when $identity is \"asmith@example.com\")

Example: asmith@example.com

When generating search tokens for non-authenticated users, using anonymous@coveo.com as a security identity name is generally a good idea.

provider (string, required)

The security identity provider containing the security identity to impersonate.

Example: Email Security Provider

type (string, optional)

The type of the security identity to impersonate.

Default: User

Allowed values:

  • User

  • Group

  • VirtualGroup

  • Unknown

Body of a search token creation call in which you specify the type of a user ID

{
  "userIds": [
    {
      "name": "asmith@example.com",
      "provider": "Email Security Provider",
      "type": "User"
    }
  ]
}

filter (string, optional)

The filter query expression to apply when authenticating a query with this search token.

This expression will be merged with the constant part of the query expression (cq) using an AND operator.

The filter can also be used in query pipeline condition statements (e.g., when $constantQuery contains \"@source==KnowledgeBase\").

Example: @source==KnowledgeBase

Enforcing a filter in search tokens is a very secure way to limit the scope of queries. However, using this approach implies that you must update server-side code to manage filter expressions.

Another more flexible, albeit less secure approach is to define query pipeline filter rules (see Adding and Managing Query Pipeline Filters).

Depending on your needs, either query filtering approach can be legitimate. However, you should stick to the same approach for all search interfaces across your solution.

Body of a search token creation call in which you specify a constant query expression

{
  "filter": "@source==KnowledgeBase",
  "userIds": [
    {
      "name": "asmith@example.com",
      "provider": "Email Security Provider",
    }
  ]
}

pipeline (string, optional)

The name of the query pipeline to use when authenticating a query with this search token.

This query pipeline will take precedence over the possible output of all other query pipeline routing mechanisms when using this search token (see Query Pipeline Routing Mechanisms and Rules).

Example: InternalSearch

Rather than enforcing a pipeline in the search token, consider enforcing a searchHub and using the condition-based query pipeline routing mechanism (see the searchHub property).

Body of a search token creation call in which you specify an enforced query pipeline

{
  "pipeline": "InternalSearch",
  "userIds": [
    {
      "name": "asmith@example.com",
      "provider": "Email Security Provider",
    }
  ]
}

searchHub (string, optional)

The name of the search hub to enforce when authenticating a query with this search token.

This value will override the searchhub parameter of the query itself, and will be passed as the originLevel1 property value when logging usage analytics search events.

The search hub can also be used in query pipeline condition statements (e.g., when $searchhub is \"CommunityHub\").

Example: SupportHub

Ideally, you should enforce the searchHub in the search token, and base the condition of each query pipeline in your Coveo Cloud organization on the searchHub value (see Condition-Based Routing (Recommended)).

Body of a search token creation call in which you specify an enforced searchhub

{
  "searchHub": "SupportHub",
  "userIds": [
    {
      "name": "asmith@example.com",
      "provider": "Email Security Provider",
    }
  ]
}

userDisplayName (string, optional)

The userDisplayName to pass when logging usage analytics search events.

This information is leveraged in the Analytics section of the Coveo Cloud administration console.

Example: Alice Smith

Body of a search token creation call in which you specify the user display name

{
  "userDisplayName": "Alice Smith",
  "userIds": [
    {
      "name": "asmith@example.com",
      "provider": "Email Security Provider",
    }
  ]
}

userGroups (array of strings, optional)

The userGroups to pass when logging usage analytics search events.

This information is leveraged in the Analytics section of the Coveo Cloud administration console.

User groups can be also be used in query pipeline condition statements (e.g., when $groups contains \"Employees\").

Example: ["Tech support agents", "Employees"]

Body of a search token creation call in which you specify user groups

{
  "userGroups": [
    "Tech support agents",
    "Employees"
  ],
  "userIds" : [
    {
      "name": "asmith@example.com",
      "provider": "Email Security Provider",
    }
  ]
}

validFor (integer, optional)

The number of milliseconds the search token will remain valid for once it has been created.

Minimum value: 900000 (i.e., 15 minutes)

Maximum/default: 86400000 (i.e., 24 hours)

Body of a search token creation call in which you specify the expiration time

{
  "validFor": 3600000,
  "userIds": [
    {
      "name": "asmith@example.com",
      "provider": "Email Security Provider",
    }
  ]
}

Additionally, the RestTokenParams model exposes the salesforceCommunityUrl, salesforceFallbackToAdmin, salesforceUser,scope, and superUserToken attributes, while the RestUserId model also exposes the authCookie, infos, and password attributes.

Normally, you should not try to manually specify values for these attributes, as they are either intended for internal use by Coveo, or simply exposed for legacy reasons.

Response

A successful search token creation call response.

200 OK

{
  "token": "fzKjcHdjPJKJVaJ2OjK0fzK2CI6dHJ1ZSwiZXhwIjoxNDY4Njk2NzEwLCJpYXQiOjE0lQGN..."
}

Renewing Expired Search Tokens

By default, a search token expires after 24 hours (see the validFor property). This means that if an end user opens a search token-authenticated page in a web browser and does not reload the page for that period of time, the Search API will respond with:

419 - Page Expired

{
  "message": "Expired token",
  "statusCode": 419,
  "type": "ExpiredTokenException"
}

Assuming that the search page is also configured to send usage analytics events, the Usage Analytics Write API will respond with:

400 - Bad Request

{
  "message": "The provided token is not a valid token.",
  "type": "InvalidToken"
}

When using the JavaScript Search Framework, you can ensure that expired search tokens get automatically renewed1.

1: It is not currently possible to renew expired search tokens for IPX interfaces.

To do so, you need to specify a renewAccessToken function when configuring the search endpoint to use for your interface. The framework will call that function whenever a Search API or Usage Analytics Write API request returns the 419 - Page Expired HTTP status code, and reconfigure your search endpoint with the renewed token.

The renewAccessToken function takes no argument and must return a JavaScript Promise object resolving to the renewed search token (i.e., a string).

// index.ejs
document.addEventListener("DOMContentLoaded", () => {
  const renewToken = () => {
    // Granted that the call returns the search token in text/plain.
    return fetch("https://example.com/token").then((response) => {
      return response.text();
    });
  }
  Coveo.SearchEndpoint.configureCloudV2Endpoint(
    "mycoveocloudorganization",
    "<%- token %>",
    "https://platform.cloud.coveo.com/rest/search/",
    {
      renewAccessToken: renewToken
    }
  );
  Coveo.init(document.body);
})
Recommended Articles