Identity and tokens
Chat accepts one thing: a bearer token issued by the Identity Server for the Chat API. There are two kinds, one for your signed-in users and one for your backend. This page explains both, how a user gets theirs, and the one naming trap that causes most first-day errors.
Two kinds of token
Section titled “Two kinds of token”| User token | Machine token | |
|---|---|---|
| Acts as | The signed-in user (sub is the user) |
Your backend client, not a user |
| Issued by | Token exchange in your backend | The client_credentials grant |
Audience (aud) |
chat-api |
chat-api |
| Scope | chat:access |
chat:sync or granular scopes such as chat:messages.send |
| What it may do | What the user’s Chat permissions allow, in conversations they belong to | What its scopes allow, in your own tenant and application only |
| Used from | Your web or mobile app | Your backend only |
Chat reads only Authorization: Bearer <token>. It checks the signature, issuer, audience, expiry and scope, then checks the context in the URL. A missing or invalid token is a 401 invalid_token. A route that wants the other kind of token answers 403 principal_not_allowed. A missing scope answers 403 insufficient_scope.
A scope only opens the door. What a user may do comes from their Chat permissions, see Chat permissions. What a machine may do comes from its scopes, see Machine scopes.
Resource identifier versus audience
Section titled “Resource identifier versus audience”The Chat API is a resource at the Identity Server, and it has two names. Mixing them up is the most common mistake.
| Value | Example | Used by | For |
|---|---|---|---|
| Resource identifier | https://chat.centralcodestation.com/api |
Your backend | resource= on the token request: which API the token is for |
| Audience | chat-api |
Chat only | Checking the token’s aud claim |
The Identity Server looks the resource up by its identifier and writes the audience into the token. Asking for resource=chat-api fails with invalid_target. Your code never reads aud, so it needs only the identifier. A token you got for your own API has a different aud and Chat rejects it.
Token exchange
Section titled “Token exchange”One token request yields a token for one resource. Your app already holds a token for your own API, so your backend swaps it for a Chat token with OAuth token exchange (RFC 8693). The app needs no redirect, no second sign-in and no extra OAuth client.
Your app --(your API token)--> Your backend GET /chat-tokenYour backend --token exchange (backend client, resource=Chat API, scope=chat:access)--> Identity ServerYour backend <-- user token: sub = the user, aud = chat-api, no refresh tokenYour app <-- { access_token, expires_in }Your app --(Bearer user token)--> Chat APIHow it behaves:
- The user must have signed in. The Identity Server will not mint a token for an arbitrary user id. The subject token must be your own API’s access token, not an ID token.
- Short lifetime. An exchanged token lives at most 10 minutes, and never longer than the user’s own token.
- No refresh token. Renew by exchanging again. Your app caches the token and asks your route again about a minute before it expires, so the Identity Server is not called per request.
- Ordinary to Chat. The token’s
client_idis your backend client, and it also carries anactclaim that Chat ignores. Chat checks permissions and membership on every call, as for any user token. - No DPoP. Chat accepts only bearer tokens and verifies no proofs. Leave DPoP out of the exchange.
- Revocation. Signing out, disabling the user or removing them from the tenant stops further exchanges. A token already issued works until it expires, which is why it is short. Chat Administration changes can also answer 401
token_revoked.
Realtime and push use the same token. Your getAccessToken function is called again for each new realtime token and each push registration.
A token works in one context
Section titled “A token works in one context”A token issued to a client of your tenant application works only in your tenant and application. Any other context answers 403 client_context_mismatch. This applies to machine tokens and to exchanged user tokens alike. See Tenants and applications.
Chat’s own web client signs users in with its own routes. Tenant applications never call them.
Related guides
Section titled “Related guides”- Issue Chat tokens to your users
- Get the Chat token in your app
- Enable token exchange
- Register a backend client
- Other languages: tokens with curl