Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Tokens and scopes

Token format#

Every token ScaiKey issues is a signed JWT. Signing algorithm is RS256 by default; the platform can be configured for ES256. Public keys are published at the JWKS endpoint and rotated on a schedule.

Three token shapes:

  • Access token — bearer credential for API calls. Carries sub, aud, scope, and a few platform-specific claims.
  • ID token — OIDC user-identity assertion. Issued when openid is in the requested scope. Contains identity claims (email, name, groups, etc.) keyed off the requested scopes.
  • Refresh token — opaque (not a JWT), one-time-use, hash-stored in the database. Exchanged for a fresh access token at the token endpoint.

Access token claims#

A standard ScaiKey access token looks like this:

json
1
2
3
4
5
6
7
8
9
{
  "iss": "https://scaikey.scailabs.ai/tenants/{tenant-id}",
  "aud": "<client_id of the audience app>",
  "sub": "<user_id or client_id>",
  "scope": "openid profile email",
  "tenant_id": "tnt_widget0001",
  "exp": 1747584000,
  "iat": 1747580400
}

Additional claims depending on grant and app type:

Claim When set Meaning
tenant_id Tenant-scoped flows The tenant the principal belongs to
partner_id When the user has a partner role Set on partner_admin and on partner-level tokens
role Admin tokens super_admin / partner_admin / tenant_admin
client_id All flows The OAuth client_id that requested the token
app_scope Platform tokens GLOBAL / PARTNER / TENANT — the app's scope level
platform_token Set true on client_credentials tokens minted by GLOBAL apps Marker for the admin API to grant cross-tenant access
token_type (claim) client_credentials grant "client_credentials" — for consumers that distinguish service vs user tokens
act Tokens minted via Token Exchange RFC 8693 delegation chain (who exchanged on whose behalf)

Note on token_type: the OAuth response envelope also has a field called token_type, which is always "Bearer" (it describes how the token should be used, not what kind of token it is). These are different things at different layers — the response field is RFC 6749 §5.1; the JWT claim is a ScaiKey extension.

Scopes#

Standard OIDC scopes#

Scope Claims released
openid Required for OIDC. Triggers ID token issuance.
profile name, given_name, family_name, preferred_username, picture, locale, zoneinfo
email email, email_verified
groups groups — array of group slugs the user belongs to
offline_access Requests a refresh token

ScaiKey admin scopes#

Used by service integrations that call ScaiKey's own admin API. Granted only to applications whose allowed_scopes list includes them.

Scope Grants
admin:read Read access to the entire admin API (partners, tenants, users, groups, apps, sessions, audit)
admin:write Write access to the admin API
users:read, groups:read Narrower read-only scopes; useful when you want a service to mirror just one resource type
scaicore:view, scaicore:manage Reserved for ScaiCore-tier operations

These are platform-token scopes — they only do anything meaningful on JWTs minted via client_credentials for GLOBAL apps. A user JWT doesn't grant admin access just because the scope is present; the user must also have the appropriate role.

How scopes are constrained#

The token endpoint never grants more than three things permit, simultaneously:

  1. What the application's allowed_scopes column lists (registered).
  2. What the request asked for (scope=... parameter).
  3. For Token Exchange: what the subject token already had (subject_scopes).

If you ask for a scope your app isn't registered for, the token comes back narrower (or, in Token Exchange, with no overlap → invalid_scope error).

Token lifetimes#

Token Default Where it's set
Access token 3600 s (1 h) applications.token_lifetime on the audience app — configurable per application
Refresh token 30 days applications.refresh_token_lifetime
Authorization code 600 s (10 min) Platform setting (OIDC.authorization_code_ttl)
ID token Same as access Always matches the access token's exp

A super_admin can change token_lifetime per application via PATCH /api/v1/admin/applications/{id}. The change applies to the next token minted; existing tokens keep their original exp.

For Token Exchange: the exchanged token's lifetime is the target's token_lifetime, not the subject's. A long-running async worker can therefore exchange a near-expiry user token for a fresh full-lifetime audience-restricted token.

Verifying tokens#

Standard JWT verification using the public key published at the JWKS endpoint. Steps:

  1. Fetch JWKS from $SCAIKEY/api/v1/platform/.well-known/jwks.json (or the tenant equivalent).
  2. Read the kid header from the JWT to pick the right key.
  3. Verify signature.
  4. Validate iss, aud, exp, nbf (if present).
  5. Apply scope checks based on your endpoint's policy.

Don't cache the JWKS forever; refresh periodically or on any kid you don't recognize. Keys are rotated occasionally; old keys remain in the JWKS during their grace period so in-flight tokens still validate.

Updated 2026-05-17 12:20:37 View source (.md) rev 1