JWT Claims Reference
What each claim in a JSON Web Token actually means, which ones are part of the spec, and which ones your identity provider invented.
A JWT payload is just a JSON object — nothing in the format requires any particular field to be present. RFC 7519 defines seven optional registered claims with reserved names and agreed meanings, so that different systems can interpret exp or aud the same way without coordinating first. Everything else in a real-world token — role, permissions, a namespaced claim from an identity provider — is a custom claim: not part of the spec, defined by whoever issued the token.
This page is a reference for both kinds. To decode an actual token rather than read about the format, use the JWT Decoder.
Standard registered claims
All seven are technically optional per the spec — a verifier must not assume any of them are present. In practice, access tokens issued by real systems almost always include exp, and often iat and sub.
| Claim | Meaning | Example |
|---|---|---|
| issIssuer | Who issued the token — typically the auth server's URL. | "iss": "https://auth.example.com" |
| subSubject | Who the token is about — usually a stable user or account ID. | "sub": "user_8f3a1c92" |
| audAudience | Who the token is for. A string or an array — a verifier should reject a token whose aud does not name it. | "aud": "https://api.example.com" |
| expExpiration time | A NumericDate (seconds since the Unix epoch) after which the token must not be accepted. | "exp": 1755302400 |
| nbfNot before | A NumericDate before which the token must not be accepted — a future-dated activation gate, not an expiry. | "nbf": 1755298800 |
| iatIssued at | A NumericDate for when the token was created. Often used to compute a token's age. | "iat": 1755298800 |
| jtiJWT ID | A unique identifier for this specific token — useful for detecting replay or maintaining a revocation list. | "jti": "550e8400-e29b-41d4-a716-446655440000" |
Common mistakes
- Treating
nbfas a second expiry. It is the opposite — a token withnbfin the future is not valid yet, not invalid. - Assuming
audwas checked automatically. Decoding a token never verifies anything; an application that doesn’t explicitly compareaudagainst itself will accept a token minted for a completely different API. - Mixing up seconds and milliseconds. Every registered timestamp claim is a NumericDate — seconds since the epoch — but plenty of code accidentally passes JavaScript’s millisecond
Date.now()straight in. The Timestamp Converter auto-detects which unit a number is likely to be.
Real-world custom claims
Outside the seven registered names, a token can carry anything its issuer wants. Identity providers each settled on their own conventions:
| Provider | Claim | What it holds |
|---|---|---|
| Auth0 | https://your-app.com/roles (namespaced) | Custom claims Auth0 requires to be namespaced as a URI, so they can never collide with a current or future registered claim. |
| Firebase Authentication | user_id, firebase | user_id duplicates sub on ID tokens; firebase is a nested object with sign_in_provider and linked identities. |
| AWS Cognito | cognito:groups, token_use | cognito:groups lists the user's group memberships; token_use distinguishes an access token from an ID token. |
| Okta | groups | Group memberships, added via an authorization-server claim mapping rather than being part of the JWT spec itself. |
A related but separate source of “standard-looking” claims is OpenID Connect — fields like email, email_verified and name are defined by the OIDC spec, not RFC 7519, but show up constantly on ID tokens because most providers implement both specs together.
A practical example
A token combining registered claims, an OIDC-adjacent role field, and a namespaced custom claim, roughly what a real access token looks like once decoded:
{
"iss": "https://auth.example.com",
"sub": "user_8f3a1c92",
"aud": "https://api.example.com",
"exp": 1755302400,
"iat": 1755298800,
"nbf": 1755298800,
"jti": "550e8400-e29b-41d4-a716-446655440000",
"role": "editor",
"permissions": ["posts:write", "posts:delete"],
"https://example.com/org_id": "org_492"
}The encoded token below decodes to exactly that payload — paste it into the JWT Decoder to see it rendered with expiry status and claim descriptions rather than as a JSON block:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyXzhmM2ExYzkyIiwiYXVkIjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20iLCJleHAiOjE3NTUzMDI0MDAsImlhdCI6MTc1NTI5ODgwMCwibmJmIjoxNzU1Mjk4ODAwLCJqdGkiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDAiLCJyb2xlIjoiZWRpdG9yIiwicGVybWlzc2lvbnMiOlsicG9zdHM6d3JpdGUiLCJwb3N0czpkZWxldGUiXSwiaHR0cHM6Ly9leGFtcGxlLmNvbS9vcmdfaWQiOiJvcmdfNDkyIn0.not-a-real-signatureThe signature segment is a placeholder — this is a hand-built example, not a token from a real issuer, so there is nothing to verify it against. Formatting the raw JSON payload on its own is exactly what the JSON Formatter is for, and decoding the individual Base64url segments by hand is what the Base64 Encoder/Decoder does.
Decoding vs. verification
Every example on this page is decoded, not verified — and that distinction matters more than it sounds. Base64url is an encoding, not encryption: anyone holding a token can read iss, exp, a role claim, all of it, with no key required. Inspecting exp or nbf only tells you what the token claims about its own validity window — it does not confirm the token is genuine. A payload with a forged role and a broken signature decodes exactly as cleanly as one that is entirely legitimate, because decoding never looks at the signature at all.
Proving a token is trustworthy requires checking its signature against the issuer’s secret or public key, which can only be done safely on a server that holds that key — never in a browser tool, and not on this page. ToolSel’s JWT Decoder intentionally does not attempt signature verification, for the same reason: a tool that asked visitors to paste a signing secret into a website would be handing over the ability to mint valid tokens, which defeats the point of having a secret at all.