Documentation & Learning Resources
Back to Tool

JWT Claim Registry Reference

Reference Guide Reading time: 10 min

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.