Skip to content

Tokens and verification

View as Markdown

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.

TokenIssued bySigned withVerified viaCarries
Access tokenConnect, Session API, Native API, or OIDC (/redeem, /refresh, /direct-issue/*, /token)The application’s active token-signing private keySession GET /applications/{applicationAnchor}/jwks.json by kidPayload iss, aud, sub, sid, jti, iat, exp
Refresh tokenConnect, Session API, Native API, or OIDC (/redeem, /refresh, /direct-issue/*, /token)The application’s active token-signing private keySame as access tokenPayload iss, aud, sid, jti, iat, exp, rotationVersion
OIDC ID tokenOIDC (/token)A platform-wide OIDC signing keyoidc.sudomimus.com/.well-known/jwks.jsonsub, iss, aud, exp, iat, at_hash, nonce?, auth_time?, amr?, acr?

All three are JWTs signed with RS256 (RSA-2048).

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.

Your application verifies signatures against its own token-signing JWK Set, published by Session API. The recommended flow is:

  1. Fetch GET https://session-api.sudomimus.com/applications/{applicationAnchor}/jwks.json and cache the response according to its Cache-Control header.
  2. For each incoming request, parse the JWT as untrusted data. Require alg === "RS256", the expected typ, aud === applicationAnchor, a non-empty kid, and a valid exp.
  3. Select the exact JWK whose kid matches the header and verify the signature. Only after verification may your application trust any claim.
  4. If the kid is 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.

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.

DefaultMinimumMaximum
Access token3 hours (10800s)60 seconds7 days (604800s)
Refresh token30 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.

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>
}
ClaimMeaning
typAlways "vnd.sudomimus.application-access+jwt". Reject any token where this differs.
subThe 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.
sidThe 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.
jtiThe unique identifier of this access-token instance. It is distinct from sid and changes whenever a new access token is issued.
issSudomimus’s HTTPS application-token issuer.
audThe applicationAnchor of the application this token was issued for.
iat, expStandard JWT issued-at / expiration, in seconds since epoch.

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.

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.

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>"
}
ClaimMeaning
issAlways "https://oidc.sudomimus.com".
subThe 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.
audThe OIDC client_id of the relying party.
exp, iatStandard JWT lifetimes, in seconds since epoch.
nonceEchoed from the relying party’s /authorize request on initial issuance. Not echoed on refresh-token grants, per OIDC core 1.0 §12.1.
auth_timeWhen the user actually authenticated, in seconds since epoch. Preserved across refresh-token grants.
at_hashBinds the ID token to the paired access token.
amr, acrAuthentication 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 /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.

  • 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.