Table of Contents
1. What Are JWT Claims?
Claims are the key-value pairs stored in the JWT payload. They represent statements about an entity (typically a user) and additional metadata. The word "claim" is used because the token claims something to be true — for example, that the user's ID is "12345" or that the token expires at a specific time.
A decoded JWT payload might look like this:
{
"iss": "https://auth.example.com",
"sub": "user_12345",
"aud": "https://api.example.com",
"exp": 1735689600,
"iat": 1735686000,
"name": "Alice Johnson",
"role": "editor",
"permissions": ["read", "write"]
}In this example, iss, sub, aud, exp, and iat are registered claims defined by the JWT specification. name, role, and permissions are private claims chosen by the application developer.
2. Registered Claims (RFC 7519)
Registered claims are predefined by the JWT specification. They are not mandatory, but when present, they must follow the defined semantics. All registered claim names are intentionally kept to three characters to keep tokens compact.
iss — Issuer
Identifies the principal that issued the JWT. This is typically a URL or string identifying your authentication server.
"iss": "https://auth.example.com"Use case: When multiple services issue tokens, the consumer can check iss to confirm the token came from a trusted authority. If the issuer doesn't match the expected value, reject the token.
sub — Subject
Identifies the principal that is the subject of the JWT. In most authentication systems, this is a unique user identifier.
"sub": "user_12345"Use case: The backend reads sub to identify which user made the request. This value should be unique within the issuer's domain and is typically a database ID or UUID.
aud — Audience
Identifies the recipients that the JWT is intended for. Can be a single string or an array of strings.
"aud": ["https://api.example.com", "https://admin.example.com"]
Use case: An API server checks that its own identifier appears in the aud claim before accepting the token. This prevents tokens issued for one service from being used at another.
exp — Expiration Time
The time after which the JWT must not be accepted for processing. The value is a Unix timestamp (seconds since January 1, 1970 UTC).
"exp": 1735689600Use case: The most critical claim for security. Short-lived access tokens (15 minutes to 1 hour) limit the damage if a token is compromised. Always validate this claim server-side.
Warning: Clock skew between servers can cause valid tokens to be rejected. Most libraries allow a small tolerance (e.g., 30 seconds) when validating expiration.
nbf — Not Before
The time before which the JWT must not be accepted. Like exp, this is a Unix timestamp.
"nbf": 1735686000Use case: Useful for tokens that should only become valid at a future time, such as pre-issued tokens for scheduled access or delayed activation scenarios.
iat — Issued At
The time at which the JWT was issued. This can be used to determine the age of a token.
"iat": 1735686000Use case: Servers can use iat to reject tokens that are too old, even if they haven't technically expired. Also useful for auditing and debugging.
jti — JWT ID
A unique identifier for the token. Used to prevent the JWT from being replayed.
"jti": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"Use case: The server stores seen jti values and rejects tokens with duplicate IDs. Essential for one-time-use tokens or when implementing token revocation.
3. Public Claims
Public claims are not registered in the JWT specification but are defined in the IANA JSON Web Token Claims Registry. They provide a collision-resistant namespace for commonly needed claims that go beyond the seven registered ones.
Common public claims from the IANA registry and OpenID Connect:
| Claim | Description | Source |
|---|---|---|
| name | Full name of the user | OpenID Connect |
| Email address | OpenID Connect | |
| email_verified | Whether the email has been verified | OpenID Connect |
| given_name | First name | OpenID Connect |
| family_name | Last name | OpenID Connect |
| picture | URL of the user's profile image | OpenID Connect |
| locale | Locale string (e.g., "en-US") | OpenID Connect |
| nonce | String value to associate a session with an ID token | OpenID Connect |
| at_hash | Access token hash value | OpenID Connect |
If you need a custom claim that might be used across organizations, consider registering it with IANA. Alternatively, use a URI-based name (e.g., "https://example.com/claims/department") to avoid collisions with other systems.
4. Private Claims
Private claims are custom claims created by the application developer to share information between parties that have agreed on their meaning. These are the most flexible and the most commonly used in practice.
{
"sub": "user_12345",
"role": "admin",
"department": "engineering",
"permissions": ["read", "write", "delete"],
"subscription_tier": "pro",
"feature_flags": {
"new_dashboard": true,
"beta_api": false
}
}Keep payloads small. Every claim adds to the token size, and JWTs are sent with every HTTP request. Large tokens increase bandwidth usage and can exceed header size limits in some servers (typically 8 KB). Only include claims that the token consumer actually needs.
5. Claim Validation in Practice
Decoding a JWT is only the first step. For security, the server must validate claims before trusting the token. Here is the typical validation sequence:
- Verify the signature — Confirm the token hasn't been tampered with using the appropriate algorithm and key
- Check
exp— Reject tokens that have expired (with a small clock-skew tolerance) - Check
nbf— If present, reject tokens that aren't yet valid - Check
iss— Verify the token was issued by a trusted authority - Check
aud— Confirm the token is intended for your service - Validate custom claims — Check roles, permissions, or other application-specific claims
Tip: Most JWT libraries handle signature verification and standard claim validation automatically. Don't implement these checks from scratch — use a well-tested library for your language.
6. Common Claim Patterns
Role-Based Access Control (RBAC)
{
"sub": "user_789",
"roles": ["editor", "reviewer"],
"org_id": "org_456"
}The server checks the roles array to determine what the user can do. Including org_id enables multi-tenant authorization.
API Scopes (OAuth 2.0)
{
"sub": "client_app_123",
"scope": "read:users write:posts",
"client_id": "mobile_app_v2"
}The scope claim (a space-separated string) defines what API operations the token grants access to. This is the standard pattern used in OAuth 2.0 access tokens.
Session Metadata
{
"sub": "user_12345",
"sid": "session_abc",
"ip": "192.168.1.100",
"device": "Chrome/120 on macOS",
"iat": 1735686000,
"exp": 1735689600
}Including session context in the token lets servers detect suspicious activity, such as a token being used from a different IP address or device than where it was issued.
7. Best Practices
- Always set
exp— Tokens without expiration are a permanent security risk if compromised. Use short lifetimes for access tokens (15 min to 1 hour). - Validate
issandaud— These claims prevent token misuse across services. A token issued by Service A should not be accepted by Service B unless explicitly intended. - Don't put sensitive data in claims — JWTs are encoded, not encrypted. Anyone with the token can read the payload. Never include passwords, credit card numbers, or secrets.
- Keep tokens small — Only include claims the consumer needs. Large tokens slow down every API request and may hit header size limits.
- Use standard claim names — Prefer registered and IANA-registered claim names over inventing your own. This improves interoperability.
- Use
jtifor sensitive operations — For one-time actions like password resets or email verification, include ajtiand track used token IDs server-side.
Inspect Your JWT Claims
Paste a token into our decoder to see all claims, formatted and color-coded.