Reference

Token Claims Reference

Complete reference for AuthPI JWT claims: access, ID, and refresh token payloads, organization claims, aud precedence rules, and TTL configuration.

Last updated 2026-07-19

AuthPI issues access tokens, ID tokens, and refresh tokens as signed JWTs. All are verifiable against the issuer’s JWKS endpoint ({AUTHPI_ISSUER_URL}/jwks.json). The JWT header contains alg (ES256, RS256, or EdDSA, per the client’s response_signature_alg) and kid; access tokens additionally declare typ: "at+jwt" (RFC 9068), so resource servers can reject non-access tokens by header alone. Refresh and ID tokens carry no typ. This page lists every claim AuthPI sets in each token type.

Access token claims

ClaimTypeDescription
issstringIssuer URL: https://idp.authpi.com/{issuer_id} (issuer IDs use the i_ prefix).
substringSubject. The user ID (usr_...) for user tokens; the client ID (c_...) or agent ID (agt_...) for client_credentials tokens.
audstring or string[]Audience. A single selected resource is a string; several selected resources are an array. See aud precedence below.
expnumberExpiration time (Unix seconds): iat + the configured TTL.
iatnumberIssued-at time (Unix seconds).
auth_timenumberSet to the token’s own iat at issuance—including access tokens minted on refresh—so it reflects issuance time, not the original login. Omitted from refresh tokens. For the user’s actual authentication time, use the ID token’s auth_time.
jtistringUnique token ID (18-character random string). Always present. Used for refresh-token rotation and replay detection.
sidstringSession ID (s_...). Present on user tokens; absent on client_credentials tokens, which have no session.
client_idstringThe client that requested the token (RFC 9068).
datobjectData attestation: { "type": "identity" | "agent" | "key" | "token" }. identity for user tokens, agent for agent client_credentials tokens. Use it as a typed guard alongside the sub prefix.
scopestringSpace-separated granted scopes, preserved across refreshes. A refresh request may narrow (never widen) the scope via the optional scope parameter (RFC 6749 §6); the rotated refresh token always retains the full grant. Defaults to "openid" when no scopes were set; omitted on agent tokens that carry no scopes.
organizationsarrayThe user’s active org memberships after selected-org and client restriction filtering, as [{ "id": "org_...", "title": null, "scopes": [...], "joined_at": 1767312000 }]. Present (possibly []) on user access tokens; omitted from refresh and client_credentials tokens.
org_idstringPresent only when the authorization request selected one organization with org=org_.... Marks that the token set is restricted to that organization.
noncestringEcho of the client-supplied nonce, when one was sent.

ID token claims

ID tokens are issued when the openid scope is granted, alongside the access token (and again on refresh). Standard claims are gated on the granted scopes per OIDC Core §5.4: the email claims require the email scope, and the profile claims require the profile scope. Within a granted scope, a claim is present only when the corresponding profile data exists.

ClaimTypeDescription
issstringIssuer URL, identical to the access token’s iss.
substringThe user ID (usr_...).
audstringAlways the client_id (OIDC Core §2). Never a custom resource audience.
expnumberExpiration (Unix seconds). The ID token lifetime is the client’s default_id_token_age, falling back to its access-token age.
iatnumberIssued-at time (Unix seconds).
auth_timenumberTime of the user’s authentication. Set by the authorization-code flow; absent on ID tokens issued via refresh.
datobjectAlways { "type": "identity" }.
emailstringThe user’s primary email address. Requires the email scope.
email_verifiedbooleanVerification status of the primary email. Requires the email scope.
namestringDisplay name. Requires the profile scope.
given_namestringFirst name. Requires the profile scope.
family_namestringLast name. Requires the profile scope.
picturestringPhoto URL. Requires the profile scope.
countrystringCountry from the user’s address, when set. Requires the profile scope.
organizationsarraySame array shape as access tokens and /userinfo: [{ "id": "org_...", "title": null, "scopes": [...], "joined_at": 1767312000 }]. AuthPI extension—not scope-gated.
org_idstringPresent only when the authorization request selected one organization with org=org_....
updated_atnumberLast profile update (Unix seconds). Requires the profile scope.
noncestringEcho of the client-supplied nonce, when one was sent.
at_hashstringAccess-token hash, binding this ID token to its access token.
acrstringAuthentication context class reference (e.g. urn:authpi:acr:password). Added by the authorization-code flow.
amrarrayAuthentication method references—the method(s) used to sign in (e.g. ["password"], ["sso"]). Added by the authorization-code flow.

Organization claim filtering

Access tokens, ID tokens, and /userinfo responses use the same organizations array shape. Refresh tokens do not carry full organization memberships.

The array is computed at issuance from the user’s current active memberships, then filtered by:

  • The optional org=org_... authorization parameter, which narrows the token set to one organization.
  • The client’s settings.restrictions.organizations policy: all, none, or allowlist.
  • Active status at both levels: the membership and the organization. Memberships of suspended organizations are excluded from newly issued tokens.

See Multi-org tokens for the propagation contract.

Refresh token claims

Refresh tokens are JWTs produced by the same generator as access tokens, with these differences:

  • aud is always the client_id, regardless of any requested resource audience.
  • auth_time is omitted.
  • organizations is omitted. Refresh re-reads current memberships instead of copying old organization claims.
  • org_id is present only when the original authorization selected an organization; refresh keeps that same restriction.
  • scope always carries the full original grant — it is the grant of record. Narrowing a refresh request (RFC 6749 §6) narrows the issued access token only; the rotated refresh token keeps the complete grant, so a later refresh can return to the full scope set.
  • The refresh-token family retains the full resource set authorized by the original authorization request as an immutable ceiling. Code exchange and each refresh choose a current access-token subset inside that ceiling. Under an optional policy, omitting resource on refresh retains the most recently selected subset; a required policy rejects omission. A later refresh may choose a different subset of the original ceiling but can never add a resource outside it.
  • The default lifetime is 7 days (default_refresh_token_age).
  • Rotation: every refresh_token grant returns a new refresh token and invalidates the old one’s jti. Reusing a rotated refresh token marks the session compromised and revokes all of its tokens.

Treat refresh tokens as opaque credentials—their claims are an implementation detail of the rotation machinery, not an API for your application.

Audience (aud) precedence

The access-token aud is resolved in this order: selected RFC 8707 resource set → legacy audienceclient_id → issuer ID. The issuer-ID fallback only applies when no client is involved; in practice all flows supply a client_id. resource and audience are mutually exclusive.

Tokenaud value
Access token (authorization code)The resource set selected at code exchange: a string for one resource or an array for several. The selection must be a subset of the authorization request. If the code exchange omits resource, it uses the full authorized set. Without RFC 8707 resources, the legacy audience or client_id applies.
Access token (refresh grant)The requested subset of the original authorization ceiling. Under an optional policy, omission retains the session’s most recently selected set; a required policy rejects omission. Without RFC 8707 resources, the original legacy audience or client_id is preserved.
Access token (client_credentials)The resource parameter, if sent; otherwise the client_id (c_... or agt_...).
Refresh tokenAlways the client_id.
ID tokenAlways the client_id.

Resource servers should verify that aud contains the exact audience they expect. Do not compare only with a scalar string: JWT libraries represent one audience as a string and several audiences as an array, and array order has no meaning. See Resource Indicators for request binding and policy.

Token lifetimes

TTLs are configured per client under settings.openid (values in seconds):

SettingApplies toDefault
default_access_token_ageAccess tokens (authorization code, refresh, and M2M client_credentials)1800 (30 minutes)
default_refresh_token_ageRefresh tokens604800 (7 days)
default_id_token_ageID tokensSame as the access-token age when unset
Agent client_credentials tokens (agt_...)Fixed 300 (5 minutes), not configurable—agents re-mint per cycle so scope changes propagate within one TTL window

All three configurable lifetimes are capped at 21 days (1814400 seconds); the API rejects any higher value. This ceiling exists because signing keys are retired ~45 days after creation, so no token may outlive its key’s publication—see JWKS & key rotation.

Example: decoded access token

A user in two organizations, with resource=https://api.example.com selected at authorization and code exchange:

{
  "iss": "https://idp.authpi.com/i_8fk2mqzr4tw1ab",
  "sub": "usr_0bk7qmxw2e9rj4t8vhzn3a5cd",
  "aud": "https://api.example.com",
  "exp": 1781262000,
  "iat": 1781260200,
  "auth_time": 1781260200,
  "jti": "Qw7Rt2Xk9Lm4Np6Zs1",
  "sid": "s_7d3f9a1c5e8b2f4d6a0c9e7b3f5d8a1c",
  "client_id": "c_0fj9qkw2tx8mre4hbz7n3vc5a",
  "dat": { "type": "identity" },
  "scope": "openid profile email",
  "organizations": [
    {
      "id": "org_0gw3hcq8r2kfn7xj9tzm4be5a",
      "title": "Founder",
      "scopes": ["owner", "billing:write"],
      "joined_at": 1767312000
    },
    {
      "id": "org_0hk2tqvw8m3rfe9pjx5zcn4ba",
      "title": null,
      "scopes": ["member", "projects:read"],
      "joined_at": 1773100800
    }
  ]
}

Example: decoded ID token

The same user and session—note the client_id audience and the same organizations array shape:

{
  "iss": "https://idp.authpi.com/i_8fk2mqzr4tw1ab",
  "sub": "usr_0bk7qmxw2e9rj4t8vhzn3a5cd",
  "aud": "c_0fj9qkw2tx8mre4hbz7n3vc5a",
  "exp": 1781262000,
  "iat": 1781260200,
  "auth_time": 1781260185,
  "dat": { "type": "identity" },
  "email": "jane@acme.example",
  "email_verified": true,
  "name": "Jane Doe",
  "given_name": "Jane",
  "family_name": "Doe",
  "picture": "https://cdn.acme.example/avatars/jane.png",
  "country": "FR",
  "updated_at": 1780531200,
  "nonce": "n-0S6_WzA2Mj",
  "at_hash": "qYrCZTjHTTjjLMZS6Wf5xg",
  "acr": "urn:authpi:acr:password",
  "amr": ["password"],
  "organizations": [
    {
      "id": "org_0gw3hcq8r2kfn7xj9tzm4be5a",
      "title": "Founder",
      "scopes": ["owner", "billing:write"],
      "joined_at": 1767312000
    },
    {
      "id": "org_0hk2tqvw8m3rfe9pjx5zcn4ba",
      "title": null,
      "scopes": ["member", "projects:read"],
      "joined_at": 1773100800
    }
  ]
}

Next steps