JWT Claim Registry Reference
This reference guide provides a comprehensive list of registered JWT claims, their meanings, data types, and proper usage. Understanding these claims is essential for implementing secure and interoperable JWT-based systems.
Registered Claims Overview
Registered claims are predefined claims specified in RFC 7519 that provide a set of useful, interoperable claims. While their use is optional, they provide standardized ways to convey common information.
Note: All timestamp-related claims use NumericDate format, which represents the number of seconds from 1970-01-01T00:00:00Z UTC until the specified UTC date/time.
Core Registered Claims
| Claim | Name | Type | Required | Description |
|---|---|---|---|---|
iss |
Issuer | String or URI | No | Principal that issued the JWT |
sub |
Subject | String or URI | No | Principal that is the subject of the JWT |
aud |
Audience | String, URI, or Array | No | Recipients that the JWT is intended for |
exp |
Expiration Time | NumericDate | No | Time after which the JWT expires |
nbf |
Not Before | NumericDate | No | Time before which the JWT must not be accepted |
iat |
Issued At | NumericDate | No | Time at which the JWT was issued |
jti |
JWT ID | String | No | Unique identifier for the JWT |
Detailed Claim Descriptions
Issuer (iss)
The "iss" claim identifies the principal that issued the JWT. This claim should be a StringOrURI value that uniquely identifies the issuer.
Valid Examples:
// String identifier
"iss": "my-auth-service"
// URI identifier
"iss": "https://auth.example.com"
// Domain-based identifier
"iss": "example.com"
Usage Tips:
- Use consistent issuer values across your application
- Consider using URIs for better uniqueness
- Validate the issuer claim on token verification
- Use different issuers for different environments (dev, staging, prod)
Subject (sub)
The "sub" claim identifies the principal that is the subject of the JWT. The subject value must be unique within the issuer's context or globally unique.
Valid Examples:
// User ID
"sub": "1234567890"
// Username
"sub": "john.doe@example.com"
// UUID
"sub": "550e8400-e29b-41d4-a716-446655440000"
// URI format
"sub": "https://example.com/users/1234567890"
Usage Tips:
- Use immutable identifiers when possible
- Don't use personally identifiable information directly
- Ensure uniqueness within the issuer's scope
- Consider using UUIDs for better privacy
Audience (aud)
The "aud" claim identifies the recipients that the JWT is intended for. This can be a single string/URI or an array of strings/URIs.
Valid Examples:
// Single audience
"aud": "my-api-service"
// Multiple audiences
"aud": ["api-service", "admin-panel", "mobile-app"]
// URI-based audience
"aud": "https://api.example.com"
Usage Tips:
- Always validate the audience claim
- Use specific audience values for different services
- Consider token substitution attacks when using multiple audiences
- Document your audience naming conventions
Expiration Time (exp)
The "exp" claim identifies the expiration time on or after which the JWT must not be accepted for processing.
Valid Examples:
// Expires in 1 hour (3600 seconds from Unix epoch)
"exp": 1516242622
// Expires on specific date
"exp": 1672531199 // December 31, 2022 23:59:59 UTC
Usage Tips:
- Always include expiration times for security
- Use short expiration times for access tokens (15-30 minutes)
- Account for clock skew between systems
- Implement token refresh mechanisms for longer sessions
Not Before (nbf)
The "nbf" claim identifies the time before which the JWT must not be accepted for processing.
Valid Examples:
// Valid starting from specific time
"nbf": 1516239022
// Valid immediately (current time)
"nbf": 1516239022
Usage Tips:
- Use for scheduled token activation
- Helpful for pre-issued tokens
- Account for clock skew in validation
- Consider timezone differences
Issued At (iat)
The "iat" claim identifies the time at which the JWT was issued.
Valid Examples:
// Token issued at specific time
"iat": 1516239022
Usage Tips:
- Useful for token age validation
- Helps with clock skew handling
- Can be used for token freshness checks
- Valuable for audit trails
JWT ID (jti)
The "jti" claim provides a unique identifier for the JWT. This can be used to prevent JWT replay attacks.
Valid Examples:
// UUID format
"jti": "550e8400-e29b-41d4-a716-446655440000"
// Random string
"jti": "abc123def456"
// Timestamp-based ID
"jti": "1516239022-user123"
Usage Tips:
- Use for token revocation mechanisms
- Ensure uniqueness across all tokens
- Consider using UUIDs or cryptographically secure random values
- Store JTIs for blacklisting capabilities
Public Claims Registry
In addition to registered claims, there are public claims that are registered in the IANA JSON Web Token Claims Registry. Here are some commonly used public claims:
| Claim | Name | Type | Description |
|---|---|---|---|
name |
Full Name | String | End-user's full name |
given_name |
Given Name | String | Given name(s) or first name(s) |
family_name |
Family Name | String | Surname(s) or last name(s) |
email |
Email Address | String | End-user's preferred e-mail address |
email_verified |
Email Verified | Boolean | True if email address has been verified |
phone_number |
Phone Number | String | End-user's preferred telephone number |
address |
Address | Object | End-user's preferred postal address |
picture |
Picture | String (URL) | URL of the end-user's profile picture |
website |
Website | String (URL) | URL of the end-user's website |
gender |
Gender | String | End-user's gender |
birthdate |
Birth Date | String (ISO 8601) | End-user's birthday |
zoneinfo |
Time Zone | String | End-user's time zone |
locale |
Locale | String | End-user's locale |
updated_at |
Updated At | NumericDate | Time user information was last updated |
Custom Claims Best Practices
Private Claims
Private claims are custom claims created to share information between parties that agree on using them.
Examples:
{
// Application-specific claims
"role": "admin",
"permissions": ["read", "write", "delete"],
"organization": "acme-corp",
"plan": "premium",
// Use namespaces to avoid collisions
"https://example.com/claims/department": "engineering",
"example.com/user_type": "employee"
}
Naming Conventions
- Use descriptive, lowercase names with underscores
- Avoid conflicts with registered and public claims
- Consider using namespaces (URIs) for organization-specific claims
- Document all custom claims in your API documentation
Security Considerations
- Never include sensitive information in claims
- Keep JWT payloads small for performance
- Validate all claims during token verification
- Consider using JWE for confidential claims
Complete JWT Example
Here's an example of a JWT with properly formatted claims:
{
// Registered claims
"iss": "https://auth.example.com",
"sub": "550e8400-e29b-41d4-a716-446655440000",
"aud": ["api.example.com", "mobile.example.com"],
"exp": 1516242622,
"nbf": 1516238022,
"iat": 1516238022,
"jti": "abc123def456",
// Public claims
"name": "John Doe",
"email": "john.doe@example.com",
"email_verified": true,
"picture": "https://example.com/avatars/john.jpg",
// Private claims
"role": "admin",
"permissions": ["users:read", "users:write", "posts:read", "posts:write"],
"organization": "acme-corp",
"department": "engineering"
}
Validation Examples
Node.js Claim Validation
function validateJWTClaims(payload) {
const now = Math.floor(Date.now() / 1000);
const clockSkew = 60; // 60 seconds
// Validate expiration
if (payload.exp && payload.exp <= now) {
throw new Error('Token has expired');
}
// Validate not before
if (payload.nbf && payload.nbf > now + clockSkew) {
throw new Error('Token not yet valid');
}
// Validate issued at (prevent tokens from future)
if (payload.iat && payload.iat > now + clockSkew) {
throw new Error('Token issued in the future');
}
// Validate issuer
const allowedIssuers = ['https://auth.example.com'];
if (payload.iss && !allowedIssuers.includes(payload.iss)) {
throw new Error('Invalid issuer');
}
// Validate audience
const expectedAudience = 'api.example.com';
if (payload.aud) {
const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
if (!audiences.includes(expectedAudience)) {
throw new Error('Invalid audience');
}
}
// Validate subject
if (!payload.sub) {
throw new Error('Missing subject claim');
}
return payload;
}
Python Claim Validation
import time
from datetime import datetime
def validate_jwt_claims(payload):
now = int(time.time())
clock_skew = 60 # 60 seconds
# Validate expiration
if 'exp' in payload and payload['exp'] <= now:
raise ValueError('Token has expired')
# Validate not before
if 'nbf' in payload and payload['nbf'] > now + clock_skew:
raise ValueError('Token not yet valid')
# Validate issued at
if 'iat' in payload and payload['iat'] > now + clock_skew:
raise ValueError('Token issued in the future')
# Validate issuer
allowed_issuers = ['https://auth.example.com']
if 'iss' in payload and payload['iss'] not in allowed_issuers:
raise ValueError('Invalid issuer')
# Validate audience
expected_audience = 'api.example.com'
if 'aud' in payload:
audiences = payload['aud'] if isinstance(payload['aud'], list) else [payload['aud']]
if expected_audience not in audiences:
raise ValueError('Invalid audience')
# Validate subject
if 'sub' not in payload:
raise ValueError('Missing subject claim')
return payload
Conclusion
Understanding and properly implementing JWT claims is crucial for building secure and interoperable authentication systems. Always validate claims according to your security requirements and follow established standards when possible.