Xeonr Developer Docs

Access Token Anatomy

What's inside a Xeonr Auth access token, and how to validate one.

Access tokens issued by Xeonr Auth are signed JWTs (JSON Web Tokens). You can decode one at jwt.io for debugging, but always validate the signature in production code.

{
  "alg": "RS256",
  "kid": "key-id-abc123",
  "typ": "JWT"
}
FieldDescription
algAlways RS256. Reject tokens with any other algorithm.
kidKey ID — use this to look up the correct public key from the JWKS endpoint before verifying the signature.

Claims

A typical user access token:

{
  "iss": "https://auth.xeonr.io",
  "sub": "urn:xeonr:user:12345",
  "aud": ["https://my-api.example.com", "https://auth.xeonr.io"],
  "exp": 1711580400,
  "nbf": 1711576800,
  "iat": 1711576800,
  "jti": "550e8400-e29b-41d4-a716-446655440000",
  "scope": "openid profile email my-app:read",
  "client_id": "660e8400-e29b-41d4-a716-446655440000",
  "azp": "660e8400-e29b-41d4-a716-446655440000",
  "urn:xeonr:auth:organisation_id": "org-uuid",
  "urn:xeonr:auth:application_id": "app-external-id",
  "urn:xeonr:auth:authorisation_id": "42"
}

Standard claims

ClaimDescription
issIssuer. Always https://auth.xeonr.io. Validate this matches exactly.
subSubject. A stable URN identifying the user (urn:xeonr:user:{id}) or service account (urn:xeonr:serviceaccount:{id}). Use this as your internal user identifier — never use email or username as an ID.
audAudience. An array of strings. Your resource server's identifier must be present in this array — reject the token if it isn't.
expExpiry (Unix timestamp). Reject tokens where exp is in the past.
nbfNot Before (Unix timestamp). Reject tokens used before this time.
iatIssued At (Unix timestamp).
jtiUnique token ID. Can be used to detect replay attacks if you maintain a token denylist.

Application claims

ClaimDescription
scopeSpace-separated list of granted scopes. Check this to authorise access to specific resources or actions.
client_idThe OAuth client that requested this token.
azpAuthorized party — the client the token was issued to. Same as client_id for direct grants.
urn:xeonr:auth:organisation_idThe tenant this token belongs to.
urn:xeonr:auth:application_idThe external ID of the application the token is scoped to.
urn:xeonr:auth:authorisation_idInternal authorization record ID. Useful for correlating with audit logs.

Optional claims

ClaimWhen presentDescription
authorization_detailsWhen RAR was usedArray of approved resource-level permissions. See the RAR guide.
actToken exchange onlyActor claim (RFC 8693). Contains sub of the client that delegated authority — identifies the original party in a delegation chain.
sidWhen session tracking is activeSession ID, used for OIDC Back-Channel Logout.

Validating a token

Validate every incoming token. Most JWT libraries handle this for you if configured correctly.

Step 1 — Fetch the JWKS

GET https://auth.xeonr.io/.well-known/jwks.json

Cache the key set and refresh it when you encounter an unknown kid. Do not re-fetch on every request.

Step 2 — Verify the signature

Use the public key matching the token's kid header. Reject the token if no matching key is found, or if the signature doesn't verify.

Step 3 — Validate the claims

Check all of the following:

  • alg is RS256
  • iss is exactly https://auth.xeonr.io
  • aud contains your resource server's identifier
  • exp is in the future
  • nbf is in the past (or now)

Step 4 — Check scopes

After signature and claim validation, check that scope contains the permission required for the requested operation.

function hasScope(token: DecodedToken, required: string): boolean {
  return token.scope.split(' ').includes(required);
}

Service account tokens

Service account tokens follow the same structure, with two differences:

  • sub uses the format urn:xeonr:serviceaccount:{id} instead of urn:xeonr:user:{id}
  • scope always includes service_account

Check for the service_account scope if your resource server needs to distinguish between user tokens and service account tokens.

On this page