Skip to content

Auth & sessions

The SDK never authenticates the user itself. Your app has already signed the user in; you mint a short-lived session JWT and pass it as sessionToken.

Minting the token (your backend)

Sign a JWT with the same JWT_SECRET the AW Chat backend uses on your infrastructure. Required claim: user id as userId (or sub):

ts
import jwt from "jsonwebtoken";

const token = jwt.sign(
  { userId: user.id },
  process.env.JWT_SECRET, // must match @aw-chat/server JWT_SECRET
  { expiresIn: "1h" },
);

Deliver token to the client → <AWChat sessionToken={token} />.

Never sign on the client

JWT_SECRET is a server secret. Mint tokens on your backend only — never embed the secret in the app or in demo source that ships to production users.

How the AW Chat backend validates it

Every /api/* request must carry Authorization: Bearer <token>:

  1. Verify HS256 signature against JWT_SECRET.
  2. Read userId (fallback sub).
  3. Resolve Intercom contact via user_contacts (create/enrich as needed).

Invalid / expired / missing → 401.

Production: require exp

Set REQUIRE_JWT_EXP=1 on the AW Chat server (mandatory when NODE_ENV=production). Tokens without exp are rejected. Always stamp expiresIn when minting.

Handling expiry

On 401 the SDK calls onSessionExpired and shows a session-expired panel. Refresh the token and update the prop (or remount):

tsx
<AWChat
  sessionToken={token}
  onSessionExpired={async () => {
    const fresh = await refreshSupportToken();
    setToken(fresh);
  }}
  /* … */
/>

Token contents

ClaimRequired
userId or subYes
expStrongly recommended; required if REQUIRE_JWT_EXP=1

Audience/issuer claims are not validated today — keep the secret strong and the TTL short.

AW Chat SDK — integration & platform handoff docs.