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; Workload tokens also carry act.sub |
| 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:
flowchart TD
Receive["Receive a JWT"] --> Parse["Parse as untrusted data"]
Parse --> Header{"Expected alg, typ, aud,<br/>non-empty kid, and valid exp?"}
Header -->|No| Reject["Reject"]
Header -->|Yes| Cached{"kid found in the cached<br/>application JWK Set?"}
Cached -->|Yes| Verify["Verify the signature"]
Cached -->|No| Refresh["Refresh the configured<br/>application JWK Set once"]
Refresh --> Found{"kid found now?"}
Found -->|No| Reject
Found -->|Yes| Verify
Verify --> Valid{"Signature valid?"}
Valid -->|No| Reject
Valid -->|Yes| Trust["Trust the verified claims"]
- 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:
- Account access tokens:
typ: "vnd.sudomimus.application-access+jwt" - Agent and Automation (Workload) access tokens:
typ: "vnd.sudomimus.workload-access+jwt" - Refresh tokens:
typ: "vnd.sudomimus.application-refresh+jwt"
Reject a token whose typ does not match the credential your endpoint expects.
An Account-only endpoint must reject Workload tokens even when their signatures
are valid. To accept Agents or Automations, the application must both enable the
exact authentication method
and implement Workload token verification. Enabling an authentication method
alone does not make an Account-only verifier accept actors.
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 (header) |
"vnd.sudomimus.application-access+jwt" for Account access; "vnd.sudomimus.workload-access+jwt" for Agent or Automation access. Accept only the types your endpoint supports. |
act.sub |
Present only in Workload access tokens: the Agent or Automation’s pairwise sector subject. act contains exactly sub; Account tokens must omit act. |
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. |
Workload actor identity
Section titled “Workload actor identity”The access-token example above is an Account token. A Workload token uses the
Workload typ and adds "act": { "sub": "<workload sector subject>" } to the
payload. Its top-level sub still identifies the owner Account, while act.sub
identifies the acting Agent or Automation within the sector. Validate the exact
actor shape and keep both identities when making application authorization
decisions. Neither identifier is a raw Account or Workload UUID. Authentication
and credential scope do not grant business permissions inside your application.
Workload sessions use the same refresh-token type as Account sessions. Refresh
tokens contain neither sub nor act; refreshing resolves the session’s current
authority and returns the corresponding Account or Workload access-token type.
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.