Appearance
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>:
- Verify HS256 signature against
JWT_SECRET. - Read
userId(fallbacksub). - 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
| Claim | Required |
|---|---|
userId or sub | Yes |
exp | Strongly recommended; required if REQUIRE_JWT_EXP=1 |
Audience/issuer claims are not validated today — keep the secret strong and the TTL short.
Related
- Security — secrets, production guards
- Configuration — env vars