Common JWT Claims (iss, sub, aud, exp & More)
By AZ Utils Editorial · · 11 min read
Inside every JWT is a small JSON object full of short, cryptic keys — sub, iss, aud, exp, iat. These are the token's claims, and they are where all the meaning lives. Knowing what each one means, and which ones you must validate, is the difference between an auth system that is secure and one that quietly accepts tokens it should reject. This guide explains the common JWT claims in depth, with examples and validation guidance.
It is written for developers implementing or debugging token auth, students learning the JWT standard, and engineers who want to validate tokens correctly.
What Are Claims?
A claim is simply a piece of information asserted about a subject — a key/value pair in the JWT payload. When a token says "sub": "123", it is claiming that the subject of the token is user 123. Because the token is signed, a verifier that trusts the signer can trust these claims without an external lookup.
The JWT standard (RFC 7519) divides claims into three groups:
- Registered claims — a small set of standardised, reserved names with defined meanings (like
expandiss). They are not mandatory, but using them correctly maximises interoperability. - Public claims — names you define for general use, ideally registered in the IANA JWT registry or namespaced (often as a URI) to avoid collisions.
- Private claims — custom names agreed between the parties using the token, for application-specific data like a user's role.
In short: JWT claims are the key/value statements in a token's payload. Registered claims like iss, sub, aud, exp, nbf, iat and jti have standard meanings; public and private claims carry application-specific data. Validating the right claims is essential for security.
The Registered Claims
There are seven registered claims. Each has a three-letter name (kept short to keep tokens compact):
| Claim | Name | Purpose |
|---|---|---|
iss | Issuer | Who created and signed the token |
sub | Subject | Who or what the token is about (usually the user ID) |
aud | Audience | Who the token is intended for |
exp | Expiration time | When the token stops being valid |
nbf | Not before | When the token starts being valid |
iat | Issued at | When the token was created |
jti | JWT ID | A unique identifier for the token |
iss — Issuer
The iss claim names the entity that issued the token, often a URL like "https://auth.example.com". When you verify a token, you should check that iss matches the issuer you expect, so a valid token from a different issuer is not accidentally accepted.
sub — Subject
The sub claim identifies the principal the token is about — in authentication, this is almost always the user's unique ID. It should be unique within the issuer so there is no ambiguity about who the token represents.
aud — Audience
The aud claim states the intended recipient(s) of the token, for example "example-api". A receiver should reject a token whose audience does not include itself. This prevents a token minted for one service from being replayed against another — a subtle but important protection.
exp, nbf, iat — The Time Claims
These three claims are timestamps expressed as NumericDate — the number of seconds since the Unix epoch (1 January 1970 UTC). For example, 1700000000 is a moment in November 2023.
exp(expiration) — the token is invalid at or after this time. Always validate it; an unexpired-forever token is a serious risk.nbf(not before) — the token is invalid before this time, useful for tokens that should only activate later.iat(issued at) — when the token was created, useful for auditing and for rejecting tokens that are implausibly old.
Because clocks are never perfectly in sync, validators usually allow a small leeway (a few seconds) when checking exp and nbf to avoid rejecting tokens at the boundary.
jti — JWT ID
The jti claim is a unique identifier for the token. It is mainly used to prevent replay (each token can be used once) and to support revocation via a denylist — you store the jti of revoked tokens and reject any token whose jti appears there.
Public and Private (Custom) Claims
Beyond the registered claims, you will almost always add your own. A common authentication payload might look like this:
{
"iss": "https://auth.example.com",
"sub": "123",
"aud": "example-api",
"iat": 1700000000,
"exp": 1700003600,
"role": "editor",
"email_verified": true
}
Here role and email_verified are private (custom) claims meaningful to this application. A word of caution: every claim you add is readable by anyone, because the payload is only Base64url-encoded. So include only what you need for identity and authorization, and never put sensitive data — passwords, full personal records, secrets — in a claim. Keep the payload small, too: tokens travel on every request, so bloated payloads waste bandwidth.
Validating Claims in Code
Good JWT libraries validate the standard time and audience claims for you when you pass the right options. The key is to actually supply those options:
// Node.js — jsonwebtoken
const payload = jwt.verify(token, key, {
algorithms: ["RS256"], // pin the algorithm
issuer: "https://auth.example.com", // checks iss
audience: "example-api", // checks aud
clockTolerance: 5, // seconds of leeway for exp/nbf
});
// exp and nbf are validated automatically when present
// Python — PyJWT
payload = jwt.decode(
token, key, algorithms=["RS256"],
issuer="https://auth.example.com",
audience="example-api",
leeway=5,
)
Then apply your own checks on custom claims — for example, confirming the role claim grants the action being attempted. To inspect a token's claims while developing, paste it into our JWT Decoder, which shows every claim and converts the NumericDate timestamps into readable dates.
How a Verifier Uses the Claims Together
The registered claims are not checked in isolation; a correct verifier evaluates them as a set, and only accepts the token if every relevant check passes. Picture an API receiving a request. First it verifies the signature, establishing that the token is authentic. Then it reads the iss claim and confirms the token came from the issuer it trusts — a valid signature from an unexpected issuer should still be rejected. Next it checks aud to confirm the token was actually intended for this API and not some sibling service that happens to share infrastructure. It then evaluates the time claims: the current time must be at or after nbf (if present) and strictly before exp, with a small leeway for clock drift. Only once all of these pass does it move on to the application-specific claims, such as checking that a role or scope permits the requested action.
This layered evaluation is why simply "the signature is valid" is never a sufficient answer to "should I trust this token?" Each claim closes a specific gap: iss and aud prevent tokens from being reused in the wrong place, the time claims prevent stale or not-yet-valid tokens, and your custom claims enforce authorization. Skipping any one of them leaves a door open that an attacker, or simply a buggy client, can walk through.
Designing Your Own Custom Claims
When you add private claims, a little discipline keeps tokens secure and maintainable. Favour small, stable, non-sensitive values that the receiver actually needs on every request — a user ID is already in sub, so a role, a tenant identifier or a set of scopes are typical good additions. Resist the temptation to embed rich objects or data that changes frequently; because a JWT is self-contained and lives until it expires, any data baked into it can become stale. If you put a user's permissions in the token and then revoke one, the old token still carries the old permissions until it expires, which is a subtle but real source of bugs and security gaps.
Naming matters too. Public claims intended for broad use should be namespaced — often as a URI like "https://example.com/role" — so they never collide with someone else's claim of the same name or with a future registered claim. Private claims used only between your own services can be simple names by agreement. And whatever you add, remember the golden rule from the start of this article: every claim is readable by anyone holding the token, so it must contain nothing you would mind the world seeing.
Try Our Free JWT Decoder
Reading raw claims — especially Unix timestamps — by eye is tedious. Our JWT Decoder lays them out clearly and privately.
- ✅ Shows all header and payload claims
- ✅ Converts iat, nbf and exp to human-readable times
- ✅ Runs in your browser — your token is never uploaded
👉 Decode and read your claims →
Real-World Examples
- An API access token uses
iss,aud,sub,expand ascopeclaim listing granted permissions. - An OpenID Connect ID token adds identity claims like
name,emailandemail_verifiedalongside the registered ones. - A one-time action token (password reset) uses a short
expand ajtiso it can be invalidated after a single use. - A delayed-activation token uses
nbfso it cannot be used until a future moment.
A Note on Trust, Staleness and Compactness
It is worth stepping back to appreciate what claims really represent: a snapshot of facts, frozen at the moment the token was issued and signed. The verifier trusts those facts because the signature proves they came from the issuer unchanged — but it trusts them as they were then, not as they are now. This is the deeper reason to be careful about which claims you include. A user's identity (their sub) rarely changes, so embedding it is safe. Their permissions, plan, or account status can change at any moment, so embedding those in a long-lived token risks the verifier acting on stale information. The safest custom claims are the ones least likely to change during a token's lifetime, and short token lifetimes limit how stale any claim can become.
The short, three-letter names of the registered claims reflect another design value: compactness. Because a token rides along on every single request, every byte counts, especially on mobile networks and high-traffic APIs. The standard deliberately chose terse names like iss and exp rather than verbose ones, and you should follow the same instinct with custom claims — include only what the receiver needs on each request, and prefer compact representations. A lean token is faster to transmit, cheaper to parse, and less likely to bump into header-size limits. Taken together, these two ideas — claims are a point-in-time snapshot, and tokens should stay small — explain much of why JWT payloads are designed the way they are, and they should guide every decision you make about what to put inside one.
Common Mistakes
- Not validating
exp. The most damaging omission — it lets expired tokens keep working forever. - Ignoring
audandiss. Accepting any validly signed token, regardless of who it was for or who issued it, enables token reuse across services. - Putting sensitive data in claims. The payload is readable by anyone; treat it as public.
- Misreading timestamps. NumericDate is seconds since the epoch, not milliseconds — mixing these up causes wildly wrong expiry checks.
- Bloated payloads. Stuffing large objects into claims makes every request heavier.
- No clock leeway. Rejecting tokens at the exact expiry boundary causes spurious failures across machines with slightly different clocks.
Best Practices
- Always validate
exp, and validateaudandissagainst expected values. - Use the registered claims for their intended purpose rather than inventing equivalents.
- Keep custom claims minimal and non-secret.
- Namespace public claims (e.g. as URIs) to avoid collisions.
- Allow a few seconds of clock leeway for time-based claims.
- Use
jtiwhen you need replay protection or denylist-based revocation.
Frequently Asked Questions
What are the standard JWT claims?
The seven registered claims are iss (issuer), sub (subject), aud (audience), exp (expiration), nbf (not before), iat (issued at) and jti (JWT ID). They have standard meanings defined in RFC 7519.
What does the sub claim mean?
The sub (subject) claim identifies who or what the token is about — in authentication this is almost always the user's unique ID, which should be unique within the issuer.
How is the exp claim formatted?
As a NumericDate: the number of seconds since the Unix epoch (1 January 1970 UTC). The token is considered invalid at or after that time, usually with a few seconds of clock leeway.
What is the difference between iat, nbf and exp?
iat is when the token was issued, nbf is the earliest time it is valid, and exp is the time it stops being valid. All three are NumericDate timestamps.
Can I add my own claims to a JWT?
Yes. You can add public claims (ideally namespaced to avoid collisions) or private claims agreed between the parties, such as a role. Remember the payload is readable by anyone, so never include secrets.
Which claims must I validate?
Always validate exp, and validate aud and iss against the values you expect. Validating these prevents accepting expired tokens or tokens intended for a different service or issuer.
Summary
Claims are the substance of a JWT — the signed statements a verifier relies on. The seven registered claims (iss, sub, aud, exp, nbf, iat, jti) cover identity, intended audience and timing, while public and private claims carry your application's own data. Two rules matter most: validate the security-critical claims (especially exp, aud and iss) on every request, and remember that the payload is public, so it must hold nothing secret and stay small. Get those right, lean on a good library to check the time and audience claims for you, and use a decoder to read tokens during development — and your token handling will be both correct and secure.
It helps to think of claims as a contract between the issuer and the verifier. The issuer promises, by signing the token, that the claims are true as of the issuing moment; the verifier's job is to decide which of those promises it actually requires before acting. A well-designed system writes that contract down explicitly: this API trusts tokens from this issuer, intended for this audience, that have not expired, and it reads these custom claims to make authorization decisions. Once the contract is clear, the implementation almost writes itself, and code reviews become straightforward because a reviewer can check the verification against the agreed contract claim by claim.
The opposite — a vague understanding of which claims matter — is where bugs breed. A team that has never decided whether it validates aud, or that treats exp as optional, will eventually accept a token it should have refused. So treat the claim set as a first-class part of your API's design, document the validation rules alongside the endpoints, and use a decoder during development to confirm that the tokens your system issues and accepts actually carry the claims your contract assumes. Clear thinking about claims is, in the end, clear thinking about trust. The few minutes it takes to write down your validation contract and verify it against real tokens will save hours of debugging and close off an entire category of authentication bugs before they ever reach production.
👉 Inspect a token's claims with our free tool →
Related Resources
- What Is a JWT? — the fundamentals
- JWT Authentication Explained — the auth flow
- JWT Security Best Practices — validate and protect tokens
- JWT Decoder — read claims instantly