> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# User authentication

> Generate secure user tokens to scope conversations and memory to each user in your app.

<Callout icon="flask-conical" color="#14b8a6">
  This is a **beta feature** according to [Algolia's Terms of Service ("Beta Services")](https://www.algolia.com/policies/terms/).
</Callout>

Generate a signed JSON Web Token (JWT) on your backend and send it with each request to keep user data private.

## What user authentication enables

Secure user tokens allow Agent Studio to:

* Associate each conversation with a user.
* Store and retrieve memory per user.
* Restrict access so users can only read their own conversations and memory.
* Support multi-user apps with personalized experiences.

Without a secure JWT, Agent Studio can't identify users.
As a result, it can't scope conversations and memory per user.

## JWT authentication flow

User authentication uses JSON Web Tokens (JWTs) to identify users securely:

```mermaid theme={"system"}
sequenceDiagram
    participant User as User
    participant Frontend as Your frontend
    participant Backend as Your backend
    participant API as Agent Studio API

    Note over User,API: One-time setup
    Backend->>Backend: Store authentication key from dashboard

    Note over User,API: Per-request flow
    User->>Frontend: Initiates request
    Frontend->>Backend: Request user token
    Backend->>Backend: Generate JWT with user ID
    Backend-->>Frontend: Returns JWT
    Frontend->>API: API request<br/>Header: X-Algolia-Secure-User-Token
    API->>API: Validates JWT<br/>Extracts user identity
    API-->>Frontend: Response scoped to user
```

### Key points

* JWTs are generated on your backend server, never in frontend code
* JWTs contain a user identifier in the `sub` (subject) claim
* Agent Studio validates the JWT signature using your authentication key
* All data is automatically scoped to the authenticated user

## Set up user authentication

<Steps>
  <Step title="Create an authentication key in the dashboard">
    1. Go to [Agent Studio Settings](https://dashboard.algolia.com/generativeAi/agent-studio/settings/user-authentication)
    2. Go to **User authentication** and  click **Create authentication key**
    3. In the creation box:
       1. Enter a descriptive name (for example, "Production authentication key")
       2. Choose the scope:
       * **All agents**: the key works for all agents in your application
       * **Specific agents**: select which agents this key applies to
    4. Click **Create**
    5. Copy the authentication key: it starts with `sk-alg-...`
    6. From the authentication keys table, note the value in the **ID** column
    7. Save both values securely:
       * **Key ID** (from the ID column): store in `ALGOLIA_KEY_ID` environment variable
       * **Authentication key** (the full `sk-alg-...` value): store in `ALGOLIA_SECRET_KEY` environment variable

    In the dashboard,
    select **Setup guide** for details about using authentication keys.

    <Warning>
      Keep your authentication key secure.
      Don't commit it to version control or expose it in frontend code.
      Use environment variables or a secrets management system.

      The authentication key can be found in the authentication keys table.
    </Warning>
  </Step>

  <Step title="Generate JWTs on your backend">
    Create an endpoint on your backend that generates JWTs for authenticated users.
    The token must include:

    * `sub` claim: Your user's unique identifier
    * `exp` claim: Token expiration timestamp
    * `kid` header: Your Algolia key ID

    <Tip>
      If you're using the **JavaScript** or **Python** API client, you can use the built-in [`forgeSecuredUserToken`](/doc/libraries/sdk/methods/agent-studio/forge-secured-user-token) helper instead of building the JWT manually.
    </Tip>

    <CodeGroup>
      ```python Python theme={"system"}
      import jwt
      from datetime import datetime, timedelta
      import os

      @app.post("/api/algolia-token")
      def get_algolia_token(current_user: User):
          """Generate a secure user token for the authenticated user."""
          payload = {
              "sub": current_user.id,  # User identifier from your authentication system
              "exp": int((datetime.utcnow() + timedelta(hours=24)).timestamp())
          }

          token = jwt.encode(
              payload,
              os.environ["ALGOLIA_SECRET_KEY"],
              algorithm="HS256",
              headers={"kid": os.environ["ALGOLIA_KEY_ID"]}
          )

          return {"token": token}
      ```

      ```ts TypeScript theme={"system"}
      import * as jwt from "jsonwebtoken";

      app.post("/api/algolia-token", async (req, res) => {
      	// Ensure user is authenticated
      	const payload = {
      		sub: req.user.id, // User identifier from your authentication system
      		exp: Math.floor(Date.now() / 1000) + 24 * 3600, // 24 hours
      	};

      	const token = jwt.sign(payload, process.env.ALGOLIA_SECRET_KEY!, {
      		algorithm: "HS256",
      		header: { kid: process.env.ALGOLIA_KEY_ID },
      	});

      	res.json({ token });
      });
      ```

      ```php PHP theme={"system"}
      <?php
      use Firebase\JWT\JWT;

      header('Content-Type: application/json');

      // Ensure user is authenticated
      $token = JWT::encode(
          [
              'sub' => $currentUser['id'],  // User identifier from your authentication system
              'exp' => time() + (24 * 3600)  // 24 hours
          ],
          $_ENV['ALGOLIA_SECRET_KEY'],
          'HS256',
          $_ENV['ALGOLIA_KEY_ID']
      );

      echo json_encode(['token' => $token]);
      ```

      ```java Java theme={"system"}
      import io.jsonwebtoken.Jwts;
      import io.jsonwebtoken.SignatureAlgorithm;
      import java.util.Date;

      @PostMapping("/api/algolia-token")
      public Map<String, String> getAlgoliaToken(@AuthenticationPrincipal User currentUser) {
          // Ensure user is authenticated
          long expirationTime = System.currentTimeMillis() + (24 * 3600 * 1000L);

          String token = Jwts.builder()
              .setSubject(currentUser.getId())  // User identifier from your authentication system
              .setExpiration(new Date(expirationTime))
              .setHeaderParam("kid", System.getenv("ALGOLIA_KEY_ID"))
              .signWith(SignatureAlgorithm.HS256, System.getenv("ALGOLIA_SECRET_KEY"))
              .compact();

          return Map.of("token", token);
      }
      ```
    </CodeGroup>

    <Warning>
      Security requirements:

      * Generate tokens only on your backend server
      * Require users to authenticate first
      * Never expose your authentication key (`ALGOLIA_SECRET_KEY`) in frontend code
      * Use HTTPS for all token transmission
      * Set appropriate token expiration times
    </Warning>
  </Step>

  <Step title="Use tokens in API requests">
    Include the JWT in the `X-Algolia-Secure-User-Token` header when making Agent Studio API requests:

    ```sh Command line icon=square-terminal theme={"system"}
    # Example: Start a conversation with user authentication
    curl -X POST "https://$ALGOLIA_APPLICATION_ID.algolia.net/agent-studio/1/agents/$AGENT_ID/completions" \
      -H "X-Algolia-Application-Id: $ALGOLIA_APPLICATION_ID" \
      -H "X-Algolia-API-Key: $ALGOLIA_API_KEY" \
      -H "X-Algolia-Secure-User-Token: $SECURE_USER_TOKEN" \
      -H "Content-Type: application/json" \
    -d '{
        "messages": [
          {
            "role": "user",
            "content": "Hello"
          }
        ]
      }'
    ```

    **Frontend implementation example:**

    ```js JavaScript icon=code theme={"system"}
    // Get token from your backend
    const userToken = await fetch("/api/algolia-token", {
    	method: "POST",
    	credentials: "include", // Include session cookies
    })
    	.then((r) => r.json())
    	.then((d) => d.token);

    // Use token in Agent Studio requests
    const appID = "ALGOLIA_APPLICATION_ID";
    const apiKey = "ALGOLIA_SEARCH_API_KEY";
    const agentId = "AGENT_ID";

    const response = await fetch(
    	`https://${appID}.algolia.net/agent-studio/1/agents/${agentId}/completions`,
    	{
    		method: "POST",
    		headers: {
    			"X-Algolia-Application-Id": appID,
    			"X-Algolia-API-Key": apiKey,
    			"X-Algolia-Secure-User-Token": userToken, // User-scoped access
    		},
    		body: JSON.stringify({
    			messages: [{ role: "user", content: "Hello" }],
    		}),
    	},
    );
    ```

    If you use the InstantSearch Chat widget with `agentId`,
    pass the token with `requestOptions.headers`:

    <CodeGroup>
      ```jsx React icon=code theme={"system"}
      <Chat
        agentId="AGENT_ID"
        requestOptions={{
          headers: {
            "X-Algolia-Secure-User-Token": userToken,
          },
        }}
      />
      ```

      ```js JavaScript icon=code theme={"system"}
      chat({
        container: "#chat",
        agentId: "AGENT_ID",
        requestOptions: {
          headers: {
            "X-Algolia-Secure-User-Token": userToken,
          },
        },
      });
      ```
    </CodeGroup>

    Use this option only with the built-in `agentId` path.
    If you use a custom `transport`,
    add the secure user token to that transport's headers.
  </Step>
</Steps>

## Security

Follow these security recommendations to reduce the risk of leaked keys,
improperly scoped users, and expired tokens in production.

### Token expiration

* Set reasonable expiration times (for example, 24 hours)
* Implement token refresh logic in the frontend
* Handle expired token errors gracefully

### Authentication key management

* Store authentication keys in environment variables or secrets managers
* Rotate keys periodically (create new keys and delete old ones from the dashboard)
* Use different keys for different environments (development, staging, production)
* Use specific agent scoping when possible to limit key access

### User validation

* Always verify user identity on your backend before generating tokens
* Include only necessary user identifiers in the `sub` claim
* Don't include sensitive user data in JWT payloads (they're not encrypted)

### HTTPS requirement

* Always transmit tokens over HTTPS
* Secure your backend token generation endpoint
* Validate Cross-Origin Resource Sharing (CORS) settings to prevent unauthorized domains from accessing tokens

## Common patterns

### Token refresh

Handle token expiration by refreshing tokens automatically,
without prompting users to sign in again:

```js JavaScript icon=code theme={"system"}
async function getValidToken() {
	let token = localStorage.getItem("algolia_token");
	let expiry = localStorage.getItem("algolia_token_expiry");

	// Check if token is expired or will expire soon (within 5 minutes)
	if (!token || !expiry || Date.now() > parseInt(expiry) - 5 * 60 * 1000) {
		// Fetch new token from backend
		const response = await fetch("/api/algolia-token", {
			method: "POST",
			credentials: "include",
		});
		const data = await response.json();

		token = data.token;
		expiry = Date.now() + 24 * 3600 * 1000; // 24 hours from now

		localStorage.setItem("algolia_token", token);
		localStorage.setItem("algolia_token_expiry", expiry.toString());
	}

	return token;
}
```

### Error handling

Handle authentication errors appropriately:

```js JavaScript icon=code theme={"system"}
try {
	const response = await fetch(apiUrl, {
		headers: {
			"X-Algolia-Secure-User-Token": await getValidToken(),
		},
	});

	if (response.status === 401) {
		// Token invalid or expired - clear and retry
		localStorage.removeItem("algolia_token");
		localStorage.removeItem("algolia_token_expiry");
		// Redirect to login or retry with new token
	}
} catch (error) {
	console.error("Authentication error:", error);
}
```

## Common issues

**"Invalid signature" error:**

* Verify your authentication key (the full `sk-alg-...` value) matches the one from the dashboard
* Ensure you're using HS256 algorithm
* Check that the `kid` header matches your key ID (from the ID column in the authentication keys table)

**"Token expired" error:**

* Implement token refresh logic
* Check that your server's clock is synchronized (JWT uses timestamps)
* Verify the `exp` claim is set correctly

**User data not being scoped:**

* Confirm you're including the `X-Algolia-Secure-User-Token` header
* Verify the `sub` claim contains a valid user identifier
* Check that the token is being generated with the correct user ID

## See also

* For user-scoped conversation history, see [Conversations](/doc/guides/algolia-ai/agent-studio/how-to/conversations)
* How memory works and when to use it, see [Memory overview](/doc/guides/algolia-ai/agent-studio/how-to/memory/overview)
* Manage tool-level credentials, see [Tools security](/doc/guides/algolia-ai/agent-studio/how-to/tools/security) - Manage tool-level credentials
* Recommended security configuration, see [Security](/doc/guides/security/security-best-practices)
