Tokens and verification
Sudomimus issues three kinds of token, signed by different keys and verified through different mechanisms. This page is the single reference for verification and token contents — what the claims mean and the rules that decide their values.
At a glance
Section titled “At a glance”| Token | Issued by | Signed with | Verified via | Carries |
|---|---|---|---|---|
| Access token | Connect, Session API, Native API, or OIDC (/redeem, /refresh, /direct-issue/*, /token) | The application’s active token-signing private key | Session GET /applications/{applicationAnchor}/jwks.json by kid | Payload iss, aud, sub, sid, jti, iat, exp |
| Refresh token | Connect, Session API, Native API, or OIDC (/redeem, /refresh, /direct-issue/*, /token) | The application’s active token-signing private key | Same as access token | Payload iss, aud, sid, jti, iat, exp, rotationVersion |
| OIDC ID token | OIDC (/token) | A platform-wide OIDC signing key | oidc.sudomimus.com/.well-known/jwks.json | sub, iss, aud, exp, iat, at_hash, nonce?, auth_time?, amr?, acr? |
All three are JWTs signed with RS256 (RSA-2048).
Access and refresh tokens
Section titled “Access and refresh tokens”These are the tokens your application backend deals with day to day. Both are signed by Sudomimus using a keypair that is specific to your application — every application has its own pair, and rotating one application’s key has no effect on any other.
Verification
Section titled “Verification”Your application verifies signatures against its own token-signing JWK Set, published by Session API. The recommended flow is:
- Fetch
GET https://session-api.sudomimus.com/applications/{applicationAnchor}/jwks.jsonand cache the response according to itsCache-Controlheader. - For each incoming request, parse the JWT as untrusted data. Require
alg === "RS256", the expectedtyp,aud === applicationAnchor, a non-emptykid, and a validexp. - Select the exact JWK whose
kidmatches the header and verify the signature. Only after verification may your application trust any claim. - If the
kidis unknown, refresh the JWK Set once immediately before rejecting. Do not derive a fetch URL from an untrusted audience; use your configured application anchor and Session API origin.
The JWK Set may contain a prepublished next key, the active signer, and retiring keys that still verify outstanding tokens. Revoked and retention-expired keys are omitted. This overlap is what lets key rotation preserve verification for tokens already in circulation.
Emergency revocation removes a retiring key from fresh Session JWKS responses
immediately. A verifier that already cached that public key may continue
accepting its signatures for the remainder of the advertised cache lifetime,
up to 300 seconds after its last successful fetch. This bounded convergence is
part of offline JWT verification; do not interpret revocation as instantaneous
at every cache-honoring verifier. Use Session POST /introspect in addition to
signature verification when a sensitive operation requires current live
session authority.
Application-token JWKS is intentionally per application, separate from the platform-wide OIDC JWKS. A verifier only receives keys belonging to its configured applicationAnchor, and rotating one application has no effect on another.
The typ header
Section titled “The typ header”Sudomimus access and refresh JWTs carry an explicit typ protected-header field:
- Access tokens:
typ: "vnd.sudomimus.application-access+jwt" - Refresh tokens:
typ: "vnd.sudomimus.application-refresh+jwt"
Reject a token whose typ does not match the credential your endpoint expects.
TTL bounds
Section titled “TTL bounds”| Default | Minimum | Maximum | |
|---|---|---|---|
| Access token | 3 hours (10800s) | 60 seconds | 7 days (604800s) |
| Refresh token | 30 days (2592000s) | 1 day (86400s) | 365 days (31536000s) |
Per-rule and per-inquiry overrides are subject to these bounds. When multiple TTLs apply (e.g. one from a Layer 1 rule and another from a Layer 3 inquiry constraint), Sudomimus folds them by taking the minimum. The access TTL is then reduced, if necessary, so that it never exceeds the refresh TTL.
Access token claims
Section titled “Access token claims”The protected header is deliberately small. Registered JWT claims and session
bindings live in the payload. sub is the pairwise sector subject and the user
key your application should use; sid is the stable logical
ApplicationSession id; jti identifies one bearer instance. No application
token contains profile data or a raw account id.
// JWT header{ "alg": "RS256", "kid": "<signing-key identifier>", "typ": "vnd.sudomimus.application-access+jwt"}// JWT payload{ "iss": "https://sudomimus.com", "aud": "<applicationAnchor>", "sub": "<sector subject>", "sid": "<application session identifier>", "jti": "<access token identifier>", "iat": <epoch>, "exp": <epoch>}| Claim | Meaning |
|---|---|
typ | Always "vnd.sudomimus.application-access+jwt". Reject any token where this differs. |
sub | The application-visible sector subject — a per-(account × sector) opaque identifier (e.g. sub_9SQ5535CRWNDDM2T). This is the value to key your users on. Stable for a given user within your sector, but rotatable by the user, and different across sectors. Treat it as opaque — do not parse it. |
sid | The stable identifier of the logical ApplicationSession. Access and refresh tokens issued for one login share it across refresh rotations. It is not a user id. |
jti | The unique identifier of this access-token instance. It is distinct from sid and changes whenever a new access token is issued. |
iss | Sudomimus’s HTTPS application-token issuer. |
aud | The applicationAnchor of the application this token was issued for. |
iat, exp | Standard JWT issued-at / expiration, in seconds since epoch. |
Refresh token claims
Section titled “Refresh token claims”The refresh token uses the same minimal JOSE header. Its payload deliberately
omits sub and all profile fields; sid, jti, and rotationVersion identify
one exact signed refresh version.
// JWT header{ "alg": "RS256", "kid": "<signing-key identifier>", "typ": "vnd.sudomimus.application-refresh+jwt"}// JWT payload{ "iss": "https://sudomimus.com", "aud": "<applicationAnchor>", "sid": "<application session identifier>", "jti": "<refresh token version identifier>", "iat": <epoch>, "exp": <epoch>, "rotationVersion": 1}rotationVersion is a positive integer that increases by one on each successful
rotation. Store the newly returned refresh JWT as one opaque credential; do not
edit or derive a token from sid, jti, or the version. Sudomimus verifies the
signature, strongly loads the ApplicationSession by sid, and exact-matches
the application, jti, and version before it rotates anything. The internal
session separately proves the current subject authority.
Application UserInfo
Section titled “Application UserInfo”Use GET https://session-api.sudomimus.com/userinfo with Authorization: Bearer <accessToken> to fetch current consent-gated profile values. The
response always carries sub and may carry email, email_verified, name,
given_name, family_name, picture, and the private claim
picture_animated. Treat these fields as live,
replaceable profile data; key users only by payload sub.
Use GET https://session-api.sudomimus.com/claim-state with the same Bearer
token when you need the live policy requirement and consent state without the
profile values. Its claim keys are email, given_name, family_name,
picture, and picture_animated.
OIDC ID tokens
Section titled “OIDC ID tokens”If your application is integrated as an OIDC relying party, the /token endpoint additionally returns an id_token alongside access_token. The ID token is signed by a platform-wide OIDC signing key (not your per-application key) and is verified against the JWKS at https://oidc.sudomimus.com/.well-known/jwks.json.
ID token claims follow the OpenID Connect standard:
{ "iss": "https://oidc.sudomimus.com", "sub": "<sector subject>", "aud": "<client_id>", "exp": <epoch>, "iat": <epoch>, "at_hash": "<access-token hash>", "nonce": "<from /authorize, if provided>", "auth_time": <epoch, if applicable>, "amr": ["<authentication method>"], "acr": "<authentication context>"}| Claim | Meaning |
|---|---|
iss | Always "https://oidc.sudomimus.com". |
sub | The sector subject — the same per-(account × sector) value carried as payload sub on the paired access token. Stable for a user within your sector, rotatable, and different across sectors. |
aud | The OIDC client_id of the relying party. |
exp, iat | Standard JWT lifetimes, in seconds since epoch. |
nonce | Echoed from the relying party’s /authorize request on initial issuance. Not echoed on refresh-token grants, per OIDC core 1.0 §12.1. |
auth_time | When the user actually authenticated, in seconds since epoch. Preserved across refresh-token grants. |
at_hash | Binds the ID token to the paired access token. |
amr, acr | Authentication method references and authentication context. |
OIDC signing keys are rotated periodically; the JWKS always publishes the keys for both the currently-active and the recently-retired key so verification continues to work during rotation.
OIDC access tokens
Section titled “OIDC access tokens”OIDC /token returns the same minimal per-application access token described
above. It contains no scope-dependent profile fields. Your application verifies
it against the per-application Session JWKS and fetches scoped identity claims
from the discovered OIDC /userinfo endpoint.
See OIDC relying parties for the full RP flow, including /userinfo and /end-session.
When to use which
Section titled “When to use which”- Connect protocol plus Session API (access / refresh tokens via the per-application Session JWKS): your own application backend talks to Sudomimus directly. Lowest overhead, no extra hop.
- OIDC (ID tokens via JWKS): your application uses an off-the-shelf OIDC library, or you want to integrate with a third party that already speaks OIDC. Sudomimus acts as the IdP.
You generally pick one or the other per application — there is no requirement to use both.