--- title: Use search token authentication slug: '56' canonical_url: https://docs.coveo.com/en/56/ collection: build-a-search-ui source_format: adoc --- # Use search token authentication [search tokens](https://docs.coveo.com/en/1346/) are special [JSON web tokens](https://jwt.io/) that can be used to execute [queries](https://docs.coveo.com/en/231/) and send [Coveo Analytics events](https://docs.coveo.com/en/260/) as a specific [user](https://docs.coveo.com/en/250/). They're intended to be used in JavaScript code running in a web browser, typically along with [Coveo Headless](https://docs.coveo.com/en/lcdf0493/), [Coveo Atomic](https://docs.coveo.com/en/lcdf0264/), or the [Coveo JavaScript Search Framework](https://docs.coveo.com/en/187/). By default, search tokens [automatically expire](#validfor-integer-optional) after 24 hours. You can [generate search tokens](#request-a-search-token) in server-side code by calling a REST service exposed through the [Coveo Platform](https://docs.coveo.com/en/186/). Typically, you'll want to use search token [authentication](https://docs.coveo.com/en/2120/) when your search page users are authenticated and some (or all) of the [items](https://docs.coveo.com/en/210/) in your [index](https://docs.coveo.com/en/204/) are secured. In this scenario, each [user](https://docs.coveo.com/en/250/) gets a unique search token, allowing the [search interface](https://docs.coveo.com/en/2741/) to securely return only the [items](https://docs.coveo.com/en/210/) that the [user](https://docs.coveo.com/en/250/) is allowed to see. > **Important** > > The examples on this page use the standard Coveo endpoint (`platform.cloud.coveo.com`). > > If your organization is [multi-region](https://docs.coveo.com/en/2976#multi-region-deployments), has [data residency outside the US](https://docs.coveo.com/en/2976#data-residency), or is [HIPAA-compliant](https://docs.coveo.com/en/1853/), you must instead use [the relevant endpoint](https://docs.coveo.com/en/1445/). ## Sample usage workflow Here's a typical workflow demonstrating the use of search tokens: . A user requests a Coveo-powered [search interface](https://docs.coveo.com/en/2741/) from a web server. . The web server executes server-side code that eventually renders the HTML response (PHP, ASP.NET, and so on). . Server-side code authenticates the [user](https://docs.coveo.com/en/250/) who is making the request. . Server-side code calls a REST service exposed through the [Coveo Platform](https://docs.coveo.com/en/186/) to [get a search token](#request-a-search-token) for the authenticated [user](https://docs.coveo.com/en/250/). . The search token is used to generate the JavaScript code that [initializes](https://docs.coveo.com/en/331#configuring-a-new-search-endpoint) the Coveo-powered [search interface](https://docs.coveo.com/en/2741/). . The server sends the generated HTML to the client. . The JavaScript code initializes the [search interface](https://docs.coveo.com/en/2741/) and executes the first [query](https://docs.coveo.com/en/231/), using the provided search token. . The [Coveo Platform](https://docs.coveo.com/en/186/) executes the [query](https://docs.coveo.com/en/231/) as the [user](https://docs.coveo.com/en/250/) [impersonated](https://docs.coveo.com/en/2737/) by the search token. . Results are displayed to the user. ## Node.js example You could implement a Node.js Express [middleware function](https://expressjs.com/en/guide/writing-middleware.html) and application to serve a Coveo-powered [search interface](https://docs.coveo.com/en/2741/) configured with a valid search token. Here's a middleware function that [requests a search token](#request-a-search-token) for the authenticated [user](https://docs.coveo.com/en/250/): ```js // ./middleware.js export const getSearchToken = async (req, _res, next) => { const orgId = ''; <1> const apiKey = ''; <2> const userIds = [ { name: 'jsmith@coveo.com', <3> provider: 'Email Security Provider', }, ]; <4> const tokenEndpoint = `https://${orgId}.org.coveo.com/rest/search/v2/token?organizationId=${orgId}`; <5> try { const request = { userIds, }; const response = await fetch(tokenEndpoint, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', Accept: 'application/json', }, body: JSON.stringify(request), }); if (response.ok) { const body = await response.json(); req.token = body.token; <6> req.orgId = orgId; <7> next(); } else { const errorBody = await response.text(); next( new Error( `Failed to retrieve search token: ${response.statusText} (${response.status})\n${errorBody}` ) ); } } catch (error) { console.log('error', error); next(error); } }; ``` <1> Replace this with your organization id. <2> Replace this value with an [API Key]({site-baseurl}:1718) that has the "Authenticated Search" scope. Important: Never store your API Key in code. <3> In practice, this information would be derived from the incoming request. <4> For details about constructing this list of `userIds`, please refer to the [API reference documentation]({site-baseurl}/13/api-reference/search-api#tag/Search-V2/operation/token-userIds). <5> You may need to use a [different endpoint]({site-baseurl}/1445/), depending on your organization. <6> If the request to generate a token has succeeded, inject the token into the request payload so it can be consumed by the template. <7> Return the `orgId` defined in this file as part of the request payload to avoid defining it in multiple places. The following Express Server calls the preceding middleware function when the `/` route is requested, and passes the generated [search token](https://docs.coveo.com/en/1346/) to the template to render: ```js // ./index.js import express from 'express'; import {getSearchToken} from './middleware.js'; const app = express(); app.set('view engine', 'ejs'); <1> app.get('/', getSearchToken, (req, res) => { res.render('search', { coveoHost: 'https://static.cloud.coveo.com/searchui', <2> orgId: req.orgId, <3> token: req.token, <4> }); }); app.listen(3000); ``` <1> Set the view engine for the express instance, which is `ejs` in this example. <2> Pass the hostname for the Coveo assets to avoid repetition. <3> Pass the organization id that was injected by the middleware. <4> Pass the search token generated for this request to use in calls to the Coveo Search API. The search endpoint must then be configured with the search token before serving the page and sending queries to the Coveo Search API. To initialize a [Headless](https://docs.coveo.com/en/lcdf0493/) [search interface](https://docs.coveo.com/en/2741/), see the [Headless usage documentation](https://docs.coveo.com/en/headless/latest/usage#configure-a-headless-engine). To initialize an [Atomic](https://docs.coveo.com/en/lcdf0264/) [search interface](https://docs.coveo.com/en/2741/), see the [Atomic usage documentation](https://docs.coveo.com/en/atomic/latest/usage#use-components-to-create-a-search-interface). The following is a basic template for a [JavaScript Search Framework](https://docs.coveo.com/en/187/)-powered search page: ```html
``` <1> If you have a HIPAA-compliant {organization}, you must [modify your initialization code]({site-baseurl}/331#configuring-a-new-search-endpoint) If the requested page includes a [Coveo In-Product Experience (IPX)](https://docs.coveo.com/en/3160/) interface, the token must be injected into the loader script URL as the value of the `access_token` [query](https://docs.coveo.com/en/231/) parameter. The following is a basic template for a page that includes an IPX interface: ```html ``` <1> Replace `` with the appropriate value. ## Request a search token To request a search token, make a POST request to `+https://.org.coveo.com/rest/search/v2/token+`. To test the service on the [Coveo Platform](https://docs.coveo.com/en/186/), use the [Swagger UI](https://platform.cloud.coveo.com/docs?urls.primaryName=Search%20API#/Search%20V2/token). The caller must [authenticate](https://docs.coveo.com/en/2120/) using an [authenticated search API key](https://docs.coveo.com/en/1718#create-an-api-key). If you're using an API key that enforces a specific [search hub](https://docs.coveo.com/en/1342/) value, you won't have to specify a `searchHub` value in the request parameter when creating the search token, as it will enforce the same value as the one in the API key. > **Important** > > Never expose your authenticated search API key in client-side code. > Always request search tokens in secure, server-side code. ### Sample request ```http POST https://.org.coveo.com/rest/search/v2/token?organizationId= HTTP/1.1 ​ Content-Type: application/json Accept: application/json Authorization: Bearer **********-****-****-****-************ ``` Payload: ```json { "userIds": [ { "name": "asmith@example.com", "provider": "Email Security Provider", "type": "User" } ] } ``` ### Successful response **200 OK** ```json { "token": "fzKjcHdjPJKJVaJ2OjK0fzK2CI6dHJ1ZSwiZXhwIjoxNDY4Njk2NzEwLCJpYXQiOjE0lQGN..." } ``` ## Request body properties > **Important** > > The `RestTokenParams` model exposes the `salesforceCommunityUrl`, `salesforceFallbackToAdmin`, `salesforceUser`, `scope`, and `superUserToken` attributes. > > The `RestUserId` model exposes the `authCookie`, `infos`, and `password` attributes. > > Don't specify your own values for these attributes. > They're either intended for internal use by Coveo or exposed for legacy compatibility. 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](https://docs.coveo.com/en/240/) to [impersonate](https://docs.coveo.com/en/2737/) when [authenticating](https://docs.coveo.com/en/2120/) a [query](https://docs.coveo.com/en/231/) with this search token. #### `name` (string, _required_) The name of the [security identity](https://docs.coveo.com/en/240/) to [impersonate](https://docs.coveo.com/en/2737/). This value can also be used in [query pipeline condition](https://docs.coveo.com/en/2793/) statements (for example, `+when $identity is "asmith@example.com"+`). See [Manage query pipeline conditions](https://docs.coveo.com/en/1959#manage-conditions) for more details. **Example:** `+asmith@example.com+` > **Leading practice** > > When generating a search token for a non-authenticated user, use `anonymous` as a [security identity](https://docs.coveo.com/en/240/) `name`. > > Doing so will automatically set the `anonymous` parameter to `true` when logging [events](https://docs.coveo.com/en/260/) with that token. #### `provider` (string, _required_) The [security identity provider](https://docs.coveo.com/en/242/) containing the [security identity](https://docs.coveo.com/en/240/) to [impersonate](https://docs.coveo.com/en/2737/). **Example:** `Email Security Provider` #### `type` (string, _optional_) The type of the [security identity](https://docs.coveo.com/en/240/) to [impersonate](https://docs.coveo.com/en/2737/). **Default:** `User` **Allowed values**: * `User` * `Group` * `VirtualGroup` * `Unknown` ### `dictionaryFieldContext` (object, _optional_) The [dictionary field](https://docs.coveo.com/en/2036/) [context](https://docs.coveo.com/en/1345/) to enforce when [authenticating](https://docs.coveo.com/en/2120/) a [query](https://docs.coveo.com/en/231/) with this search token. This value will override the `dictionaryFieldContext` parameter of the [query](https://docs.coveo.com/en/231/) itself. A dictionary [field](https://docs.coveo.com/en/200/) [context](https://docs.coveo.com/en/1345/) is a key-value store in which each pair corresponds to the name of a dictionary [field](https://docs.coveo.com/en/200/) to query, along with the key to target within that [field](https://docs.coveo.com/en/200/). For example, suppose that in your [index](https://docs.coveo.com/en/204/), the `@price` dictionary [field](https://docs.coveo.com/en/200/) contains different values for its `storeA` and `storeB` keys. Including `"dictionaryFieldContext": { "price": "storeA" }` when creating a search token means that, for any [query](https://docs.coveo.com/en/231/) made with that search token, any part of the [query](https://docs.coveo.com/en/231/) expression that targets the `@price` [field](https://docs.coveo.com/en/200/) will in fact only [query](https://docs.coveo.com/en/231/) the `storeA` values of that [field](https://docs.coveo.com/en/200/). > **Important** > > If you make a dictionary field [free-text searchable](https://docs.coveo.com/en/1833#free-text-search), all its values are indexed as free-text searchable. > You should therefore avoid making dictionary fields free-text searchable if you want the values to remain secret. ### `allowedDictionaryFieldKeys` (object, _optional_) Specifies which [dictionary field](https://docs.coveo.com/en/2036/) keys users can access when using [field aliases](https://docs.coveo.com/en/q1k90356/). Unlike `dictionaryFieldContext`, which selects a single key per dictionary field, field aliases let users retrieve multiple keys from the same dictionary field in a single request. The `allowedDictionaryFieldKeys` property controls which keys users are authorized to access. The format is `{ "": ["**"] }`, where `["**"]` grants access to all keys in the specified dictionary field. > **Note** > > Fine-grained access control for specific keys (for example, `["regular", "vip"]`) is planned for a future release. > Currently, only the wildcard `["*"]` is supported. > **Important** > > You can't use `allowedDictionaryFieldKeys` (field aliases) and `dictionaryFieldContext` in the same request. > Doing so returns an error. #### `allowedDictionaryFieldKeys` sample call Here's the body of a search token creation call in which you authorize access to all keys in the `price_list` dictionary field: ```json { "userIds": [ { "name": "asmith@example.com", "provider": "Email Security Provider" } ], "allowedDictionaryFieldKeys": { "price_list": ["*"] } } ``` ### `filter` (string, _optional_) The [search result filter](https://docs.coveo.com/en/2740/) to apply when [authenticating](https://docs.coveo.com/en/2120/) a [query](https://docs.coveo.com/en/231/) with this [search token](https://docs.coveo.com/en/1346/). When provided, this [query](https://docs.coveo.com/en/231/) expression is a mandatory filter that is merged with the constant part of the [query](https://docs.coveo.com/en/231/) expression ([`cq`](https://docs.coveo.com/en/179/)) using an `AND` operator at the end of the [query pipeline](https://docs.coveo.com/en/180/) execution. Therefore, the `filter` can't be used in [query pipeline condition](https://docs.coveo.com/en/2793/) statements, since those conditions are evaluated before the `filter` token is applied. For example, a condition such as `when $constantQuery contains "@source==KnowledgeBase"` won't take into account the `filter` value of `@source==KnowledgeBase`. > **Leading practice** > > Enforcing a filter in [search tokens](https://docs.coveo.com/en/1346/) is a secure way to limit the scope of [queries](https://docs.coveo.com/en/231/), since the filter is mandatory and can't be altered client-side. > However, this approach implies that the filter can't be used to drive conditional logic in [query pipelines](https://docs.coveo.com/en/180/). > > A more flexible, albeit less secure, approach is to define [query pipeline](https://docs.coveo.com/en/180/) [filter rules](https://docs.coveo.com/en/3410/). > > Depending on your needs, either [query](https://docs.coveo.com/en/231/) filtering approach can be legitimate. > However, you should stick to the same approach for all [search interfaces](https://docs.coveo.com/en/2741/) across your solution. #### `filter` sample call Here's the body of a search token creation call in which you specify a [constant query expression](https://docs.coveo.com/en/179/): ```json { "filter": "@source==KnowledgeBase", "userIds": [ { "name": "asmith@example.com", "provider": "Email Security Provider" } ] } ``` ### `pipeline` (string, _optional_) The name of the [query pipeline](https://docs.coveo.com/en/180/) to use when [authenticating](https://docs.coveo.com/en/2120/) a [query](https://docs.coveo.com/en/231/) with this search token. This [query pipeline](https://docs.coveo.com/en/180/) will take precedence over the possible output of all other [query pipeline routing mechanisms](https://docs.coveo.com/en/1666/) when using this search token. Unless you specify a [`queryPipeline`](https://docs.coveo.com/en/1502#querypipeline-string) parameter when logging search or click [events](https://docs.coveo.com/en/260/), the [Coveo Analytics](https://docs.coveo.com/en/182/) service will use the `pipeline` value defined in the search token. **Example:** `InternalSearch` > **Leading practice** > > Rather than enforcing a `pipeline` in the search token, consider enforcing a [`searchHub`](#searchhub-string-optional) and using the condition-based [query pipeline](https://docs.coveo.com/en/180/) routing mechanism. #### `pipeline` sample call Here's the body of a search token creation call in which you specify an enforced [query pipeline](https://docs.coveo.com/en/180/): ```json { "pipeline": "InternalSearch", "userIds": [ { "name": "asmith@example.com", "provider": "Email Security Provider" } ] } ``` ### `searchHub` (string, _optional_) The name of the [search hub](https://docs.coveo.com/en/1342/) to enforce when [authenticating](https://docs.coveo.com/en/2120/) a [query](https://docs.coveo.com/en/231/) with this search token. This value will override the `searchhub` parameter of the [query](https://docs.coveo.com/en/231/) itself, and will be passed as the `[originLevel1](https://docs.coveo.com/en/1337/)` property value when logging [Coveo Analytics](https://docs.coveo.com/en/182/) search [events](https://docs.coveo.com/en/260/). The [search hub](https://docs.coveo.com/en/1342/) can also be used in [query pipeline condition](https://docs.coveo.com/en/2793/) statements (for example, `when $searchHub is "CommunityHub"`). See [Manage query pipeline conditions](https://docs.coveo.com/en/1959#manage-conditions) for more details. **Example:** `SupportHub` > **Leading practice** > > Ideally, you should enforce the `searchHub` in the search token, and [base the condition](https://docs.coveo.com/en/1666#condition-based-routing-recommended) of each query pipeline in your Coveo organization on the `searchHub` value. > > However, this isn't recommended for search tokens that are used in Coveo for Commerce solutions. > See [Authenticate commerce requests](https://docs.coveo.com/en/o8ld0051/) for more information. #### `searchHub` sample call Here's the body of a search token creation call in which you specify an enforced [search hub](https://docs.coveo.com/en/1342/): ```json { "searchHub": "SupportHub", "userIds": [ { "name": "asmith@example.com", "provider": "Email Security Provider" } ] } ``` ### `userDisplayName` (string, _optional_) The `userDisplayName` to pass when logging [Coveo Analytics](https://docs.coveo.com/en/182/) search [events](https://docs.coveo.com/en/260/). This information is leveraged in the **Analytics** section of the [Coveo Administration Console](https://docs.coveo.com/en/183/). **Example:** `Alice Smith` #### `userDisplayName` sample call Here's the body of a search token creation call in which you specify the [user](https://docs.coveo.com/en/250/) display name: ```json { "userDisplayName": "Alice Smith", "userIds": [ { "name": "asmith@example.com", "provider": "Email Security Provider" } ] } ``` ### `userGroups` (array of strings, _optional_) The `userGroups` to pass when logging [Coveo Analytics](https://docs.coveo.com/en/182/) search [events](https://docs.coveo.com/en/260/). This information is leveraged in the **Analytics** section of the [Coveo Administration Console](https://docs.coveo.com/en/183/). User [groups](https://docs.coveo.com/en/202/) can be also be used in [query pipeline condition](https://docs.coveo.com/en/2793/) statements (for example, `when $groups contains "Employees"`). See [Manage query pipeline conditions](https://docs.coveo.com/en/1959#manage-conditions) for more details. **Example:** `["Tech support agents", "Employees"]` #### `userGroups` sample call Here's the body of a search token creation call in which you specify user [groups](https://docs.coveo.com/en/202/): ```json { "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` (that is, 15 minutes) **Maximum/default:** `86400000` (that is, 24 hours) #### `validFor` sample call Here's the body of a search token creation call in which you specify the expiration time: ```json { "validFor": 3600000, "userIds": [ { "name": "asmith@example.com", "provider": "Email Security Provider" } ] } ``` ## Renew expired search tokens By default, a search token [expires](#validfor-integer-optional) after 24 hours. If a user opens a page that's [authenticated](https://docs.coveo.com/en/2120/) with a search token and doesn't reload it for this length of time, the Search API will respond with the following error: **419 - Page Expired** ```json { "message": "Expired token", "statusCode": 419, "type": "ExpiredTokenException" } ``` If the search page is also configured to send [events](https://docs.coveo.com/en/260/), the Usage Analytics Write API will respond with the following error: **400 - Bad Request** ```json { "message": "The provided token is not a valid token.", "type": "InvalidToken" } ``` You can usually renew expired search tokens automatically.[.footnote]^[[1](#ipx-note)]^ In [Headless](https://docs.coveo.com/en/lcdf0493/), you can use the `renewAccessToken` option when you [configure your engine](https://docs.coveo.com/en/headless/latest/usage#configure-a-headless-engine). In [Atomic](https://docs.coveo.com/en/lcdf0264/), you can use the `renewAccessToken` option when you [initialize the `atomic-search-interface` component](https://docs.coveo.com/en/atomic/latest/usage#use-components-to-create-a-search-interface). In the [JavaScript Search Framework](https://docs.coveo.com/en/187/), specify a [`renewAccessToken`](https://coveo.github.io/search-ui/interfaces/isearchendpointoptions.html#renewaccesstoken) function when configuring the search endpoint to use for your interface. The [JavaScript Search Framework](https://docs.coveo.com/en/187/) will call this function whenever a Search API or Usage Analytics Write API request returns the `419 - Page Expired` HTTP status code. It will then reconfigure your search endpoint with the renewed token. > **Note** > > The `renewAccessToken` function doesn't take an argument. > It must return a JavaScript [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) object that resolves to the renewed search token (that is, a string). ```js ``` <1> This example assumes that the search token returned is in `text/plain` format. -- 1. It's not currently possible to renew expired search tokens for IPX interfaces. --