---
title: "Generate a JWT for AI Assistant | Nutrient"
canonical_url: "https://www.nutrient.io/guides/ai-assistant/viewer-integration/client-authentication/generate-a-jwt/"
md_url: "https://www.nutrient.io/guides/ai-assistant/viewer-integration/client-authentication/generate-a-jwt.md"
last_updated: "2026-06-18T13:13:55.949Z"
description: "JSON Web Tokens (JWTs) used for authentication by AI Assistant can be generated with one of the many open source libraries that are available."
---

# Generate a JWT

JSON Web Tokens (JWTs) used for authentication by AI Assistant can be generated with one of the many open source libraries that are available and listed on [jwt.io](https://jwt.io/).

## Token requirements

- It has to include the standard claim `"exp"`, which sets the deadline for the validity of the token. This needs to be a non-negative number using the [Unix “Seconds Since the Epoch” timestamp format](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_15).

- Additionally, you can restrict the documents and sessions a user can access.
  - `"document_ids"` are the identifiers of a Document Engine document. This field is an optional array of strings with each document a user should have access to. If this claim is omitted (`undefined`), the user can access any document held on Document Engine. If the claim is present but there’s an empty array (`[]`), the user is explicitly restricted from accessing any documents. Supplying one or more IDs limits access to only those documents.
  - `"session_ids"` is an array of session IDs. These IDs define the sessions a user can access. If this claim is omitted, the user can access any session data. This ID is a unique string passed to the [Nutrient Web SDK configuration](https://www.nutrient.io/guides/ai-assistant/viewer-integration/nutrient-web-sdk.md)

- You can identify the user making the request by including a `user_id` claim.
  - `"user_id"` is an optional non-empty string that identifies the user. If provided, it must match the `userId` in the client configuration — a mismatch will return a `401` error. The `user_id` is stored as thread metadata.

- It’s also possible to throttle a user’s usage. The `user_id` claim must be set in the JWT to implement rate limiting.
  - `"request_limit"` is an optional object that defines the maximum number of requests a user can make in a given time period. If this claim is omitted, the user can make an unlimited number of requests.
    - `"requests"` is the maximum number of requests a user can make in the given time period.
    - `"time_period_s"` is the time period in seconds in which the user can make the maximum number of requests.

- You can control which agent configuration model labels can be overridden by the client.
  - `"agent_configuration"` is an optional object used to authorize runtime agent configuration overrides.
  - `"agent_configuration.model_overrides"` is an optional object that maps model labels (for example, `"default-llm"`) to an allowlist of models.
  - You can use the `"*"` key as a fallback for any label that isn’t explicitly listed.
  - Supported allowlist values:
    - Exact model (for example, `"openai:gpt-5-mini"`)
    - Provider wildcard (for example, `"openai:*"`)
    - Full wildcard (`"*"`), which allows any model or provider

Example:

```json

{
  "agent_configuration": {
    "model_overrides": {
      "default-llm": ["openai:gpt-5-mini", "anthropic:*"],
      "*": ["openai:*"]
    }
  }
}

```

## Generating tokens

The following example shows the creation of a JWT in JavaScript using the [`jsonwebtoken`](https://github.com/auth0/node-jsonwebtoken) library.

1. Create a key via `ssh-keygen`:

   ```shell

   ssh-keygen -t rsa -b 4096 -f jwtRS256.key
   # Enter your passphrase.

   # Get the public key in PEM format:

   openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256_pub.pem

   # If the above command fails because newer versions of `ssh-keygen` output a different format,

   # convert the key to PEM like this and then repeat the `openssl` command.

   ssh-keygen -p -m PEM -t rsa -b 4096 -f jwtRS256.key
   openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256_pub.pem
   ```

   The private key (`jwtRS256.key`) is used to sign the tokens you hand out to the clients.

   The public key (`jwtRS256_pub.pem`) needs to be added as a `JWT_PUBLIC_KEY` in [AI Assistant’s configuration](https://www.nutrient.io/guides/ai-assistant/service-configuration/docker-configuration.md) so that the server will be able to validate the tokens’ signatures but won’t be able to create valid signatures. This example assumes you chose the `RS256` algorithm as the `JWT_ALGORITHM` in AI Assistant’s configuration.

   If you want to quickly test Nutrient Web SDK with your application, you can also use the key from our [example apps](https://github.com/PSPDFKit/pspdfkit-server-example-nodejs/blob/master/config/jwt.pem) (passphrase: _secret_). Make sure to change to a self-generated key before going into production.

2. Install the `jsonwebtoken` dependency:

   ```shell

   npm install --save jsonwebtoken
   ```

3. Read the private key so that it can be used to sign JWTs. In the claims, pass the set of permissions you want to have, along with the expiration. You can then use the resulting token in your application:

   ```js

   const fs = require("fs");
   const jwt = require("jsonwebtoken");
   const key = fs.readFileSync("./jwtRS256.key");
   const token = jwt.sign(
     {
       document_ids: ["abc"],
       user_id: "user-abc-123",
       agent_configuration: {
         model_overrides: {
           "default-llm": ["openai:gpt-5-mini", "anthropic:*"],
           "*": ["openai:*"],
         },
       },
     },
     {
       key,
       passphrase: "YOUR_PASSPHRASE_GOES_HERE",
     },
     {
       algorithm: "RS256",
       expiresIn: 60 * 60, // 1 hour — this will set the `exp` claim for us.
     },
   );
   ```
---

## Related pages

- [Secure client authentication for AI Assistant](/guides/ai-assistant/viewer-integration/client-authentication.md)

