Try the JWT Decoder

JWT Claims: The Seven Registered Ones and Why aud Matters

Seven registered JWT claims have defined meanings; everything else is yours and is ignored unless you check it. What each claim does and which ones libraries skip.

September 22, 2026 8 min read
Share: Facebook WhatsApp LinkedIn Email
JWT Claims: The Seven Registered Ones and Why aud Matters

The Claim Nobody Registered

Decode a JWT and you get a payload full of three-letter keys:

{
  "iss": "https://auth.example.com",
  "sub": "user_88213",
  "aud": "https://api.example.com",
  "exp": 1789012345,
  "iat": 1789008745,
  "nbf": 1789008745,
  "jti": "a3f9c2e1-4b7d-4e8a-9c1f-2d5b8e0a7f43",
  "role": "editor",
  "org": 4471
}

Seven of those are registered claims — defined in the JWT specification with agreed meanings. Two are yours, invented for your application.

The distinction matters more than it looks, because libraries validate the registered ones automatically and ignore the rest entirely. A claim you invented has exactly the security value of a comment unless your code explicitly checks it.

The Seven Registered Claims

iss — issuer

Who created and signed this token. A string or URI identifying the authorisation server.

Validate it. In a system with multiple identity providers, an attacker who obtains a valid token from a less-trusted issuer can present it to a service that only checks the signature. If the service accepts any signature it can verify, and it holds keys for several issuers, the token passes.

sub — subject

Who the token is about. Usually a user ID, and typically opaque and stable rather than an email address, which can change.

Should be unique within the issuer's namespace. The combination of iss and sub uniquely identifies a principal globally.

aud — audience

Who the token is for. A string or array of strings identifying the intended recipients.

This is the most frequently unvalidated claim, and one of the most important. If your API accepts a token issued for a different service — a token the user legitimately obtained for a third-party app that shares your identity provider — that app can now call your API as the user.

Every service must check that its own identifier appears in aud and reject the token if it doesn't. Many libraries only perform this check if you pass the expected audience explicitly.

exp — expiration time

Unix timestamp, in seconds, after which the token must not be accepted.

Note the unit. JavaScript's Date.now() returns milliseconds, and the resulting bug — a token that appears to expire fifty thousand years from now — is common enough to be worth checking for explicitly.

nbf — not before

Unix timestamp before which the token must not be accepted. Used for tokens issued in advance of when they become valid.

Less commonly used than exp but validated by most libraries when present.

iat — issued at

When the token was created. Useful for age policies — you might accept a token that hasn't expired but still reject it if it was issued more than a certain time ago, for example when re-authentication is required for a sensitive operation.

jti — JWT ID

A unique identifier for this specific token. Enables replay detection and selective revocation: maintain a list of revoked jti values and check incoming tokens against it.

Rarely used, because maintaining that list reintroduces the server-side state JWTs were meant to avoid.

Clock Skew

Time-based claims compare against the verifier's clock, and clocks disagree.

A token issued by a server one second ahead of yours has an iat and nbf in your future. Strict validation rejects it. The user sees an authentication failure that resolves itself a second later.

Standard practice is a leeway of 30 to 60 seconds on time-based claims. Most libraries expose this as a configuration option.

jwt.verify(token, key, { 
  clockTolerance: 30,          // seconds
  audience: 'https://api.example.com',
  issuer: 'https://auth.example.com'
});

Don't set leeway too generously. A five-minute tolerance extends the usable life of every token by five minutes, including compromised ones.

Custom Claims and Namespacing

Anything not in the registered set is a private claim. Two conventions exist to avoid collisions:

Namespaced URIs, the approach several identity platforms adopt:

{
  "https://example.com/roles": ["editor", "reviewer"],
  "https://example.com/tenant": "acme-eu"
}

Verbose, and collision-free.

Short prefixed names, common in internal systems:

{
  "acme_role": "editor",
  "acme_tenant": 4471
}

Either works. What matters is avoiding names that might later become registered claims, and avoiding generic names like role or id that another system in your stack might also use with a different meaning.

Decode a token with the JWT Decoder to see exactly what claims a provider is issuing:

  1. Paste the token.
  2. Read the decoded header and payload.
  3. Identify which claims are registered and which are custom.

Note what decoding does and doesn't tell you. Decoding is not verification. The tool shows you the contents; it doesn't prove the token is authentic or unexpired. Anyone can decode a JWT — the payload is Base64url, not encryption.

Never paste production tokens into any browser tool. A JWT is a credential. Use test tokens.

Keep the Payload Small

Two practical constraints push toward minimal claims.

Size. JWTs typically travel in an Authorization header on every request. Web servers impose header size limits — commonly 8 KB total, sometimes less. A token stuffed with permissions, profile data and preferences can approach that, and the failure mode is an opaque 431 or 400 from the server rather than anything that points at the token.

Staleness. A claim in a token reflects state at issuance. If a user's role changes, tokens issued before the change still carry the old role until they expire. The longer the expiry, the longer the stale data circulates.

This is the core JWT tradeoff. Putting authorisation data in the token avoids a lookup per request; it also means revocation isn't immediate. Short expiry times narrow the window at the cost of more refresh traffic.

Practical guidance:

  • Include a stable user identifier and a small number of coarse claims
  • Don't include data that changes frequently
  • Don't include anything sensitive — the payload is readable by anyone holding the token
  • Look up fine-grained permissions server-side rather than embedding them

The Header Matters Too

The header is short and worth reading:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "2026-03-key-a"
}

alg — the signing algorithm. Never trust this value to select your verification algorithm. Configure the expected algorithm on the verifier and reject anything else. Trusting the header's alg is the basis of the algorithm-confusion class of attacks, where an attacker changes it to none or switches an RS256 token to HS256 and signs it with the public key.

kid — key identifier, telling the verifier which key to use. Essential for key rotation. Validate that the kid corresponds to a key you actually trust, rather than fetching whatever it points to.

typ — media type. Usually JWT. Checking it prevents a class of token-substitution confusion where a token intended for a different purpose is presented.

A Validation Checklist

For every token your service accepts:

  1. Verify the signature against a key you trust, using an algorithm you configured — not one the token specified.
  2. Check exp with modest clock leeway.
  3. Check nbf if present.
  4. Check iss against your expected issuer.
  5. Check aud contains your service's identifier.
  6. Check typ if you accept multiple token types.
  7. Validate custom claims explicitly. A role claim does nothing unless your code reads it and enforces it.
  8. Check jti against a revocation list if you maintain one.

Steps 4 and 5 are the ones most often skipped, and step 7 is the one people assume is automatic.

FAQ

What's the difference between registered and custom claims? Registered claims are defined in the JWT specification with agreed semantics and are validated by most libraries. Custom claims are application-specific and only meaningful if your code checks them.

Why does my token expire immediately? Almost always exp set in milliseconds instead of seconds, or the opposite — a value in seconds interpreted as milliseconds.

Do I need to validate the audience claim? Yes. Without it, a token issued for a different service that shares your identity provider may be accepted by yours.

Is the JWT payload encrypted? No. It's Base64url-encoded, which is trivially reversible. Anyone with the token can read every claim. Never put secrets in it.

How long should tokens live? Short for access tokens — minutes to an hour is typical — with a refresh token mechanism for longer sessions. The right value depends on your revocation requirements.

Should I trust the alg header? Never. Configure the expected algorithm on the verifier and reject tokens that specify anything else.

The Takeaway

A JWT's security depends on which claims get checked, and libraries check fewer than most people assume. Signature and expiry are usually automatic; issuer and audience often aren't; custom claims never are. Decoding a token to see what's actually in it is the first step toward knowing which of those checks your service is missing.

Decode and inspect JSON Web Tokens free with the JWT Decoder at sadiqbd.com — no sign-up, instant results.

Ask AI about this article
Share: Facebook WhatsApp LinkedIn Email

JWT Decoder

Free, instant results — no sign-up required.

Open JWT Decoder →
Similar Tools
JSON Formatter Cron Explainer Hash Generator HTML Entities URL Encoder/Decoder REST API Checker Base64 Encoder/Decoder Timestamp Converter
JWT Token Refresh Strategies: Access Tokens, Refresh Rotation, and When Sessions Beat JWTs
Developer
JWT Token Refresh Strategies: Access Tokens, Refresh Rotation, and When Sessions Beat JWTs
JWT vs Opaque Session Tokens: Why This Tool Can Decode Some Tokens and Not Others
Developer
JWT vs Opaque Session Tokens: Why This Tool Can Decode Some Tokens and Not Others
JWT "Stateless" Is a Half-Truth — The Microservices Patterns That Account for the Other Half
Developer
JWT "Stateless" Is a Half-Truth — The Microservices Patterns That Account for the Other Half
Most JWT Libraries Don't Check the Audience Claim — What Gets Verified, What Doesn't, and How to Handle Revocation
Developer
Most JWT Libraries Don't Check the Audience Claim — What Gets Verified, What Doesn't, and How to Handle Revocation