JWT claims reference
A JSON Web Token carries its data as claims — name/value pairs inside the JSON payload. Some claim names are registered by RFC 7519 with precise meanings (iss, exp, aud), others are standardized by OpenID Connect for identity (email, nonce, auth_time), and the rest are public or private to your application. Verifying a token is mostly about validating these claims correctly: the wrong skew on exp, a skipped aud check, or trusting an unverified email are all real-world security holes. Search the reference above by claim name or keyword.
How it works
JWT claims live in the second segment (the payload) of a header.payload.signature token, base64url-encoded JSON. After verifying the signature with the issuer’s key, a validator must check the claims: exp and nbf are NumericDate integers (seconds since the Unix epoch) bounding the validity window; iss must match a trusted issuer; aud must include your service; and jti can be tracked to block replay. OpenID Connect layers identity claims on top — nonce ties an ID token to a specific auth request, auth_time supports max_age, and azp names the authorized party. Access tokens (RFC 9068) commonly add scope, client_id, and roles for authorization decisions.
Tips and examples
- Always validate the trio that prevents misuse: signature,
exp/nbf(with small skew), andaud. Skipping any one undermines the others. - Use
subas the stable user key, neveremailorpreferred_username, which can change or collide. - For replay-sensitive flows, persist seen
jtivalues until theirexpso a token cannot be used twice. - Gate identity trust on booleans: rely on
email_verifiedbefore linking accounts, and checkazpequals yourclient_idwhenaudhas multiple values.
Validation order that matters
When a resource server receives a token, the correct order of checks is:
- Parse the header to extract
algand optionallykid. - Reject dangerous algorithms — never accept
alg: noneor symmetric HMAC when you expect asymmetric keys. - Fetch the public key from the issuer’s JWKS endpoint using
kidif present. - Verify the signature over the exact bytes of the original token.
- Check
exp— reject if current time is past it (allow a small clock skew, typically 30–60 seconds). - Check
nbf— reject if current time is before it. - Check
iss— must match the expected issuer URI exactly. - Check
aud— must contain your service’s identifier.
Skipping step 8 is the most common mistake in practice. A token legitimately issued for one API can then be replayed against a different API on the same platform. The aud claim is specifically there to prevent this.
Custom and private claims
Beyond registered and OIDC claims, applications often add their own claims — tenant_id, plan, feature_flags, org_id, and so on. The IANA guidance is to namespace these with a URI to avoid future collisions with registered names: for example "https://example.com/roles": ["admin"] rather than a bare "roles". Bare short names like role, permissions, and groups are popular in practice but risk clashing with future standardised claims. Use the URI prefix for any claims you plan to publish or share across systems.
NumericDate pitfalls
exp, nbf, iat, and auth_time use NumericDate: seconds (not milliseconds) since the Unix epoch. The most frequent bug is passing Date.now() directly — JavaScript’s Date.now() returns milliseconds, producing a value 1,000 times too large. The token appears to expire in the year ~50000 and is never rejected. Always divide by 1,000 when setting time claims from JavaScript: Math.floor(Date.now() / 1000).