Token Claims Management

Token management in Descope includes controlling the claim payloads in JWTs that are issued to users and machines.

In practice, this usually means configuring custom claims beyond standard claims like sub, iss, and exp so downstream services can make authorization and personalization decisions without extra backend calls.

For step-by-step setup details, see the Custom Claims flow action and JWT Templates docs.

Where Claims Appear in Tokens

When configured, custom claims can appear in every JWT Descope issues for the user or access key, including:

Standard Token Claims

Descope JWTs combine standard JWT claims, Descope-specific claims, and custom claims you configure.

Which claims appear depends on the token type (user or M2M), your JWT Template authorization format, and whether the user or access key has tenants, roles, or impersonation active.

Claims fall into three categories:

CategoryMeaning
Always presentIncluded on every token of that type. Your application can rely on these being there.
Context-dependentAdded when a specific condition applies — for example, the user belongs to tenants, is being impersonated, or the client SDK submitted claims.
Configuration-dependentAdded only when you set them via a JWT Template, Custom Claims flow action, or OIDC scope.

For claim categories and a full reference table, see Standard Token Claims below.

User Session and Refresh Tokens

Session and refresh tokens share the same claim payload. Custom claims you configure are stored in the refresh token and copied into each new session token on refresh.

ClaimCategoryDescription
subAlways presentSubject — the Descope User ID.
issAlways presentIssuer — your Descope Project ID, or a full OIDC issuer URL when using an OIDC-compliant JWT Template.
iatAlways presentIssued-at time (UNIX epoch seconds).
expAlways presentSession token expiration time (UNIX epoch seconds).
drnAlways presentDescope Resource Name — where the token is stored. Typically DS for Descope-managed sessions.
amrAlways presentAuthentication Methods Reference — array of methods used to authenticate. See amr values below.
rexpContext-dependentRefresh token expiration (ISO 8601). Present when a refresh token lifetime applies.
dctContext-dependentDescope Current Tenant — the user's active tenant ID. Auto-set when the user belongs to one tenant; set after tenant selection in a flow.
tenantsContext-dependentMap of tenant IDs to nested roles and permissions. Present when the user has tenant associations and your JWT template uses the default authorization format. Omitted with Current Tenant, No Tenant Reference or No Descope Claims — see Authorization Claims Configuration.
rolesContext-dependentProject-level roles, or tenant-scoped roles at the JWT root when using Current Tenant, No Tenant Reference.
permissionsContext-dependentProject-level permissions, or tenant-scoped permissions at the JWT root when using Current Tenant, No Tenant Reference.
actContext-dependentActor — present during user impersonation. act.sub holds the impersonator's User ID; sub is the impersonated user.
nsecContext-dependentNon-secure claims added by the client SDK or API. Do not trust these on your backend — see Secure vs non-secured custom claims.
audConfiguration-dependentAudience — intended recipients of the token. Set via JWT Template or Custom Claims action.
azpConfiguration-dependentAuthorized party — added when Descope acts as an OIDC provider. See Additional standard claims.
dciConfiguration-dependentDescope Consent ID - an internal ID for the consent granted by the user. Only applicable for Inbound Apps and Agentic Clients.
Custom claimsConfiguration-dependentAny application-specific keys you define via JWT Template or Custom Claims flow action. Server-set custom claims appear at the JWT root; client-set claims appear under nsec.

amr (Authentication Methods Reference)

The amr claim is an array of strings indicating which authentication methods were used for the session:

ValueMeaning
oauthOAuth social login
emailOTP, Magic Link, or Enchanted Link via email
smsOTP or Magic Link via SMS
whatsappWhatsApp nOTP
webauthnPasskeys
totpAuthenticator app (TOTP)
fedSSO / federated login (default for SSO)
pwdPassword
mfaTwo or more distinct authentication methods

You can override amr when your SSO provider includes specific authentication methods in its OIDC token.

Access Key JWTs

When an M2M Access Key is exchanged for a JWT, or a client secret is used with client_credentials flow with a Federated Application or Inbound App, the token follows the same general structure as a user session token, with a few differences:

ClaimCategoryDescription
subAlways presentSubject — the Access Key ID (not a user ID).
issAlways presentIssuer — your Descope Project ID.
iatAlways presentIssued-at time (UNIX epoch seconds).
expAlways presentToken expiration time (UNIX epoch seconds).
drnAlways presentDescope Resource Name — typically DS.
tenantsContext-dependentTenants and authorization configured on the access key. Same nesting rules as user tokens.
roles / permissionsContext-dependentAuthorization on the access key, at the root or nested under tenants depending on your Access Key JWT Template.
nsecContext-dependentClaims submitted by the M2M client during key exchange. Untrusted — use a JWT Template instead for server-verified claims.
Custom claimsConfiguration-dependentClaims from an Access Key JWT Template are inserted at the JWT root (not under nsec).

User session claims like amr, dct, and act do not apply to access key JWTs.

OIDC ID tokens

When Descope acts as an OIDC provider, ID tokens follow the OIDC specification. Standard claims are always present; profile, authorization, and custom claims require the corresponding scope.

ClaimCategoryDescription
issAlways presentIssuer — https://api.descope.com/{projectId}.
subAlways presentSubject — the Descope User ID.
audAlways presentAudience — the OIDC application Client ID.
iatAlways presentIssued-at time.
expAlways presentExpiration time.
name, email, email_verified, picture, phone_number, phone_number_verified, given_name, family_nameConfiguration-dependentStandard OIDC profile claims — included when the client requests the matching scope (profile, email, phone). See OIDC Applications.
tenantsConfiguration-dependentUser's tenants, roles, and permissions — included when the client requests the descope.claims scope.
Custom claimsConfiguration-dependentApplication-specific claims — included when the client requests the descope.custom_claims scope and claims are configured via JWT Template or Custom Claims action.
azpConfiguration-dependentAuthorized party — the Client ID that requested the token.

Two Ways to Configure Claims

Note

You can manage JWT Templates as code with our infrastructure as code (IaC) providers, such as Terraform.

There are two places you can configure claims in Descope:

MethodScopeWhen to use
JWT Template (preferred)Project-wide, code-manageableClaims that should appear on every JWT issued by a project, application, or Inbound App. Best for consistent token claims.
Custom Claims flow actionPer-flowClaims that depend on flow context — conditional logic, connector responses, step-up authentication, or values collected from the user during the flow.

It's worth noting that you can use both together. If a claim is set in both places, the Custom Claims flow action wins and overrides the JWT Template value for that flow.

Supported Claim Value Types

Custom claim values can be:

  • Strings, booleans, or numbers — simple, flat values.
  • Dynamic values — pulled from Descope user attributes, tenant attributes, or other available context. These automatically refresh when the underlying value changes (see Dynamic Claim Updates below).
  • JSON objects — for grouping related fields under a single claim key (see Nested JSON Claims below).

Both the JWT Template editor and the Custom Claims flow action support a simple mode for flat key/value entry and an advanced mode for defining the claim payload as a JSON object.

Nested JSON Claims

Note

This is useful when downstream consumers expect related data grouped under a single key (for example, gateways like Hasura or external authorization systems that read structured claim objects).

You can use a JSON object as the value of a custom claim.

Example of a custom claims action with nested JSON

In advanced mode with JWT Templates, you can set the claim key to a JSON object instead of a flat value:

{
  "my_json_custom_claim": {
    "hello": "I am a custom claim",
    "that_has": "Nested Json"
  }
}

The object is embedded directly into the issued JWT under the specified key:

{
  "amr": ["oauth"],
  "drn": "DS",
  "exp": 1776282239,
  "iat": 1776281639,
  "iss": "P32jk5Nq29jcuXmAz56iGq5uRb7m",
  "my_json_custom_claim": {
    "hello": "I am a custom claim",
    "that_has": "Nested Json"
  },
  "sub": "U3B57mYzHbJY8HFvjHvhRkTPq5xr"
}

You can nest objects multiple levels deep, mix object and scalar values within the same template, and combine static fields with dynamic user-attribute references inside the same nested structure.

Dynamic Claim Updates

Claim values backed by a Descope user or tenant attribute update automatically whenever the underlying attribute changes and the user's session is refreshed. You don't need to re-issue tokens manually.

This is useful for:

  • Real-time access control — role or permission changes take effect on the next refresh.
  • Reflecting up-to-date profile data (display name, locale, preferences) without an extra API call.
  • Multi-tenant context — tenant_id, tenant.name, or tenant-specific attributes that shift when a user switches tenants.

Dynamic values can be used inside both flat and nested claim definitions.

Claim Limits

Each key can have a maximum of 60 characters, each claim value can have a maximum of 500 characters, and each JWT can have a maximum of 100 keys.

These limits apply across both the JWT Template and the Custom Claims flow action. Because Descope stores custom claims in the refresh token (typically held in a cookie), large or numerous claims also contribute to your overall cookie size — see JWT Claims security best practices for guidance.

Security Considerations

Custom claims are read by every service that consumes the JWT. A few things to keep in mind:

  • Don't store sensitive data: JWTs are Base64-encoded, not encrypted. Treat anything in a claim as visible to anyone who holds the token.
  • Trust only server-set claims: Claims added by a client SDK are placed under an nsec claim and should not be trusted by your backend. Claims set via JWT Templates or the Custom Claims flow action (server-side) are trusted.
  • Override standard claims carefully: You can override claims like aud or amr, but doing so has implications for token validation and audit trails.

For full guidance, see JWT Claims security best practices.

Was this helpful?

On this page