JWT Guides

JWT Claims Explained: How I Read iss, aud, exp, sub and More

Understand what common JWT claims mean, how I interpret iss, aud, exp, sub and related fields, and which checks belong to application policy.

By JWTDecoder.toolsLast verified 2026-09-12

Editorial voice: first-person phrasing describes the documented JWTDecoder.tools workflow; JWTDecoder.tools is the publisher and organizational author.

iss · sub · aud · exp · nbf · iat · jti claim map
iss · sub · aud · exp · nbf · iat · jti

When I decode a JWT, I do not read every claim in random order. I use a repeatable sequence so I can answer the most important debugging questions quickly: who issued this token, who is it for, is it still active, and what does the application think the subject is allowed to do?

I usually start with the JWT Decoder, then move from standardized claims to application-specific ones.

The order I use when I inspect a JWT payload

For a payload like:

{
  "iss": "https://auth.example.com/",
  "sub": "user_123",
  "aud": "api://orders",
  "scope": "orders:read orders:write",
  "iat": 1789200000,
  "exp": 1789203600
}

I normally inspect these first:

  1. iss — who does the token claim issued it?
  2. aud — which recipient or API is it intended for?
  3. exp and nbf — is it currently usable from a time perspective?
  4. iat — when was it issued?
  5. sub — who or what is the subject?
  6. scope, roles, groups — what authorization data is present?
  7. jti, azp, auth_time, acr, amr — what extra context is useful for this protocol?

This order helps me separate authentication problems from authorization problems.

How I use iss — issuer

iss identifies the issuer.

When I see:

{
  "iss": "https://login.example.com/"
}

I compare it with the issuer the API is configured to trust. I do not trust it just because the URL looks familiar.

RFC 8725 makes the security requirement stronger than a simple string check: the cryptographic keys used to verify the JWT must belong to the issuer being trusted.

If I suspect an issuer mismatch, I use the JWT Debugger to compare the actual value with the expected value, then verify that my key source is tied to the same issuer.

How I use aud — audience

aud tells me who the token is intended for.

It can be a string:

{
  "aud": "api://orders"
}

or an array:

{
  "aud": ["api://orders", "api://reporting"]
}

If the Orders API expects api://orders but the token says api://billing, I have a strong explanation for a 401 even if the signature is valid.

This is one of the claims I inspect most often when dev, staging and production identity configurations get mixed. The JWT audience mismatch guide covers that troubleshooting flow in detail.

How I use exp — expiration

exp tells me when the token should stop being accepted.

I remember that JWT NumericDate values use seconds, not JavaScript milliseconds. If I want a quick readable timestamp, I open the JWT Expiry Checker.

I do not treat an unexpired exp as proof that the token is valid. It only tells me that one time condition has not failed yet.

The JWT exp guide explains how I check clock skew, missing expiration and unit mistakes.

How I use nbf — not before

nbf is the inverse time boundary: the token should not be accepted before that time.

When a token is not expired but is still rejected, I check whether nbf is in the future.

I also compare server clocks because a small difference between the issuer and verifier can matter near the activation boundary.

How I use iat — issued at

iat tells me when the token says it was issued.

I use it as context rather than as a universal validity rule. It helps me answer questions such as:

  • does this token belong to the login session I am actually debugging?
  • is the token unexpectedly old?
  • does the declared lifetime between iat and exp look reasonable?
  • is iat strangely far in the future?

I do not assume iat automatically creates an expiration window. That depends on application policy.

How I use sub — subject

sub identifies the subject represented by the token.

I avoid guessing what it means. It might be a user ID, service identity or another subject identifier depending on the issuer.

I also avoid combining sub values from unrelated issuers as though they share one global namespace. The pair of issuer and subject is often more meaningful than sub alone.

What I expect from jti

jti is a JWT ID.

When present, it can help a system identify one specific token. But I do not treat the presence of jti as automatic replay protection or revocation support. Those behaviors require application policy and, in many cases, server-side state.

How I treat scope, roles and groups

These claims are extremely useful during debugging, but I do not assume they have universal semantics.

For example:

{
  "scope": "profile orders:read",
  "roles": ["support", "billing"],
  "groups": ["employees"]
}

One platform may use a space-delimited scope string. Another may use namespaced permission claims. A field called roles is not automatically authoritative merely because it is readable.

When authentication succeeds but an API returns 403, these are the claims I inspect next.

The extra claims I look for in OAuth and OIDC flows

Depending on the provider and token type, I may also inspect:

  • azp — authorized party;
  • auth_time — authentication time;
  • acr — authentication context class reference;
  • amr — authentication methods references.

I use these only when the relevant protocol or provider documentation gives them meaning. A generic decoder can show the values, but it cannot know every application's business rules.

The rule I use before trusting any claim

I separate three questions:

  1. Decode: what does the payload say?
  2. Verify: does the signature match trusted key material?
  3. Validate: are issuer, audience, time claims and permissions acceptable here?

I never skip from step 1 to “this user is authenticated.”

The JWT Signature Verifier handles supported signature checks, while the JWT Validator and JWT Debugger help with common claim validation.

My practical claim-inspection checklist

When I am debugging a token, I use this order:

  1. I decode the JWT without trusting it yet.
  2. I compare iss with the configured issuer.
  3. I compare aud with the current API.
  4. I check exp, nbf and iat as one timeline.
  5. I identify sub in the context of the issuer.
  6. I inspect scopes, roles and groups only after authentication checks make sense.
  7. I verify the signature using a key source tied to the trusted issuer.
  8. I check provider-specific claims against provider documentation.

This approach keeps me from getting distracted by a large payload. I focus first on the claims that explain trust, recipient, time and authorization, then move into application-specific details.

Sources

Related guides