JWT Claims Explained: Registered, Public, and Private
Decode any JWT and you'll get a JSON object full of short, cryptic field names — iss, aud, sub, and whatever custom fields the issuer added. They're standardized enough that once you know what a handful of them mean, most tokens you'll ever encounter become readable at a glance.
Registered, public, and private claims: the three official categories
The JWT spec (RFC 7519) actually splits every claim into one of three categories, and the terminology trips people up because "public" and "private" don't mean what they'd mean for a key pair — none of this is about encryption or secrecy.
- Registered claims — the seven specific claim names defined directly in the spec itself (
iss,sub,aud,exp,nbf,iat,jti, covered claim-by-claim below). Sometimes called "reserved claims" for the same reason — the spec reserves these exact names for these exact meanings. - Public claims — claim names anyone can define and use, as long as they're registered in the IANA JSON Web Token Claims Registry (or namespaced as a collision-resistant URI) so two unrelated systems don't both invent a claim called, say,
emailwith different meanings. Most OpenID Connect claims —email,name,picture— are public claims in this sense. - Private claims — claims agreed on between specific parties with no registry involved at all — an internal
org_idorrolefield your own application invented for its own use. These carry the real risk of collision, since nothing stops another system from using the same name for something else; namespacing a private claim under a URI you control (https://yourapp.com/claims/roleinstead of justrole) is the usual defense.
In practice, most applications don't rigorously separate "public" from "private" — a claim like email gets used the same way whether or not it was formally registered with IANA. The distinction matters most when you're designing a token schema that other, unrelated systems will also need to read correctly.
The registered claims (part of the JWT spec)
These have fixed meanings defined by the JWT standard itself — any library or service that produces JWTs is expected to use them the same way:
iss(issuer) — who created and signed the token. Typically a URL or identifier for the authentication service, e.g.https://auth.example.com.sub(subject) — who the token is about, almost always a user ID. This is the "who is this token for" field.aud(audience) — who the token is intended for, i.e. which API or service should accept it. A service should reject a token whoseauddoesn't match itself, even if the signature is otherwise valid — this stops a token issued for one service from being replayed against another.exp(expiration) — a Unix timestamp after which the token should no longer be accepted, regardless of signature validity.iat(issued at) — when the token was created, as a Unix timestamp. Useful for auditing and for calculating a token's actual age.nbf(not before) — a Unix timestamp before which the token isn't valid yet. Less common thanexp, used for tokens issued slightly ahead of when they should take effect.jti(JWT ID) — a unique identifier for this specific token, useful for logging or for maintaining a revocation list of individual tokens.
Public and private claims in practice
Beyond the seven registered claims, issuers add whatever additional fields their application needs. A few show up often enough, across enough different systems, to be worth recognizing on sight:
roles/permissions/scope— what the token's holder is allowed to do. Naming and structure vary a lot between systems (a flat array of strings, a space-separated scope string, a nested object) — this is almost always a private claim, specific to one application.email/name— basic profile info included so a service doesn't need a separate lookup just to display who's logged in. These are public claims in the formal sense, defined by the OpenID Connect standard rather than invented per-application.org_id/tenant_id— common in multi-tenant systems, identifying which organization or workspace the token applies to. Private, application-specific.
Reading a real payload, claim by claim
A typical access token payload might look like this:
{
"iss": "https://auth.example.com",
"sub": "user_8f2a91",
"aud": "api.example.com",
"iat": 1735689600,
"exp": 1735693200,
"roles": ["editor"],
"org_id": "org_4471"
}
Read left to right, this says: a token issued by auth.example.com, identifying user user_8f2a91, meant only for api.example.com to accept, created at a specific moment and valid for one hour after that (exp minus iat), granting "editor" permissions within organization org_4471. Every field here is answerable on its own — the skill in reading a JWT quickly is just recognizing each abbreviation without having to look it up every time, which is really all this reference is for.
What you can and can't trust from a decoded token
Every claim listed here is only meaningful if the token's signature has actually been verified against the issuer's real signing key. Decoding shows you what a token claims; it says nothing about whether those claims are genuine. A token with a roles: ["admin"] claim and an invalid or unchecked signature proves nothing — anyone can construct a JSON payload that says whatever they want. This is why signature verification, done server-side with the actual key, is the step that makes any of these claims trustworthy.
FAQ
Are registered claims required in every JWT?
No — every registered claim is optional per the spec. In practice, most real-world tokens include at least sub, exp, and iat, since almost every JWT library defaults to setting them, but a technically valid JWT can omit all seven and just carry custom claims instead.
What's the difference between a "reserved" claim and a "registered" claim?
Nothing — they're two names for the same seven claims (iss, sub, aud, exp, nbf, iat, jti). "Registered" is the term RFC 7519 itself uses; "reserved" describes the same thing from the other direction — these specific names are reserved for these specific meanings, so using exp for something other than an expiration timestamp would violate the spec.
Can I add my own claim called "sub" with a different meaning?
Technically the JSON format won't stop you, but doing so breaks interoperability with any standard JWT library or middleware, which will read your custom-meaning sub as if it were the standard subject claim. If you need something that isn't one of the seven registered claims, give it a different, namespaced name instead of overloading a reserved one.
Try it
FreeToolDev's JWT Decoder decodes multiple tokens at once and lays out every claim clearly, with human-readable timestamps for exp/iat/nbf — entirely client-side, so nothing you paste in is sent anywhere.