Tokens & claims
Exactly what your app receives after sign-in, and how to use it for identity, organization context and permission checks.
Scopes
| Scope | Adds |
|---|---|
openid | sub, the stable user ID. Required |
profile | name, given_name, family_name, picture |
email | email, email_verified |
organization | The org claim: organization, roles, teams and permissions |
offline_access | A refresh token, so your app can renew access silently |
Example claims
With all scopes, the ID token and the userinfo response look like this:
{
"iss": "https://accounts.webrizen.com/api/auth",
"aud": "your-client-id",
"sub": "e8be9dc8-983d-493e-b16b-edb539a9b321",
"email": "ravi@spiceroute.in",
"email_verified": true,
"name": "Ravi Kumar",
"picture": null,
"org": {
"id": "0b6f2c1e-…",
"name": "Spice Route Restaurants",
"slug": "spice-route-restaurants",
"logo": null,
"roles": ["cashier"],
"teams": [{ "id": "7a1d…", "name": "Andheri branch" }],
"permissions": {
"menu": ["read"],
"order": ["read", "create", "update"],
"table": ["read"]
}
}
}The org claim
- Which organization? The one the user picked when signing in to your app (or their only
one). It's
nullif they don't belong to any organization yet. Design your app to handle that, for example with an "Ask your admin for an invite" screen. rolesare the role names exactly as defined, built-in or custom.permissionsare already resolved: the union of all roles. Check permissions, not role names, so owners can reshape roles without code changes.teamsare the member's teams in that organization.
Use sub, never email, as the key
sub never changes. Emails can be changed by the user. Store sub (and org.id) as foreign
keys in your app's database.
Token types and lifetimes
| Token | Format | Default lifetime |
|---|---|---|
| ID token | JWT, signed RS256 | 10 hours |
| Access token | Opaque string | 1 hour |
| Refresh token | Opaque string | 30 days (issued with offline_access) |
Access tokens are opaque: don't try to decode them. To learn who a token belongs to, call
/oauth2/userinfo with it, or /oauth2/introspect from a backend using the
client credentials.
Verifying an ID token yourself
Most libraries do this automatically. If you're writing it by hand, use the JWKS endpoint:
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(new URL("https://accounts.webrizen.com/api/auth/jwks"));
const { payload } = await jwtVerify(idToken, JWKS, {
issuer: "https://accounts.webrizen.com/api/auth",
audience: process.env.WEBRIZEN_SSO_CLIENT_ID,
});Keeping permissions fresh
Claims describe the user at the moment the token was issued. When an admin changes roles:
- the change applies in Webrizen SSO immediately;
- your app sees it at the next sign-in, or when it refreshes (the SDK's
/refreshroute); - for sensitive actions, re-check with
/oauth2/userinfo, which always returns current data.