Skip to content

Signing users in with JWT

Use a JSON Web Token (JWT) when the page that hosts the widget already knows who the visitor is, for example after SSO or a portal login. Each visitor then gets their own identity in SerenityGPT, and chat history shows only their own conversations.

Use a plain tenant token only when the audience is anonymous and the content is public. Anyone who reads the page source can reuse a tenant token.

How it works

  1. The SerenityGPT team gives you a signing secret for your installation.
  2. Your server signs a short-lived token for the signed-in user when it renders the page.
  3. The page passes the token to the widget in api_token. SerenityGPT checks the signature and the expiration time on every request.

The secret stays on your server. Only the signed token reaches the browser.

Warning

Anyone who has the secret can sign a token for any user and any tenant on the installation. Store it in your server environment, never in browser code or in source control.

Token claims

Claim Required Description
u Yes Stable user ID, for example portal:8f14e45f. SerenityGPT creates the user on first use. Use an ID that never changes, not an email address: changing u creates a new user without the old history.
tenant Yes Tenant code the widget is embedded for. The user searches this tenant only.
exp Yes Expiration time as a Unix timestamp in seconds, not milliseconds.
metadata No JSON object stored on the user and on each question. Send it only when you need it in reports.

Sign the token with HS256.

SerenityGPT trusts the u and tenant values in a valid token and gives the user access to that tenant. Your server decides who the user is and what they can search:

  • Take u from the signed-in session on your server. Never use an ID or a tenant sent by the browser.
  • Start u with a prefix that is unique to your identity source, followed by an ID that is never reassigned.
  • Check that the user is allowed to use the tenant before you sign the token.
  • Sign tokens for ordinary users only. Accounts with administrator access in SerenityGPT can read every conversation in their tenant.

Signing a token

Python

import os
import time

import jwt  # pip install pyjwt

def serenity_token(user_id: str) -> str:
    payload = {
        "u": f"portal:{user_id}",
        "tenant": "support",
        "exp": int(time.time()) + 10 * 3600,  # 10 hours
    }
    return jwt.encode(payload, os.environ["SERENITY_JWT_SECRET"], algorithm="HS256")

Node.js

const jwt = require('jsonwebtoken') // npm install jsonwebtoken

function serenityToken(userId) {
  return jwt.sign(
    { u: `portal:${userId}`, tenant: 'support' },
    process.env.SERENITY_JWT_SECRET,
    { algorithm: 'HS256', expiresIn: '10h' },
  )
}

Passing the token to the widget

Render the token into the page from your server:

<script>
  var SERENITY_WIDGET = {
    api_url: "https://<your-backend-url>/api/v2/",
    api_token: "{{ serenity_token }}",
    history: true
  };
</script>
<script src="https://js.serenitygpt.com/widget.js"></script>

Chat history (history: true) requires a JWT. With a tenant token, the history panel stays hidden.

Calling the API

The API accepts the same token:

curl -H "Authorization: Bearer <jwt>" https://<your-backend-url>/api/v2/settings/

Expiration

  • Set exp 8 to 12 hours ahead. The widget reads api_token once when the page loads and does not refresh it.
  • After exp, requests fail with 401 Token expired. Reloading the page issues a new token.
  • Signing out of your application does not revoke a token that is already issued. It stays valid until exp.

Tenant

  • Send tenant in every token. Without it, SerenityGPT uses the user's default tenant, which can be a different content set.
  • The user cannot switch tenants from the widget. To give a user another tenant, sign a new token with that tenant.
  • An unknown tenant code fails with 400 Tenant '<code>' not found.

Errors

Status Message Cause
401 Token expired exp is in the past.
401 Token missing expiration No exp claim.
401 Token missing username No u claim.
401 Invalid token: ... Wrong secret, wrong algorithm, or a damaged token.
400 Tenant '<code>' not found The tenant claim does not match a tenant.

Common mistakes

  • Signing tokens in browser code, which exposes the secret.
  • Setting exp in milliseconds.
  • Reusing one token for all visitors, which merges their history into one user.