JWT Custom and Private Claims
Learn how to define and use custom JWT claims for application-specific data, naming conventions to avoid collisions, and what to include versus what to exclude.
Detailed Explanation
Beyond the registered claims defined in RFC 7519 (like sub, exp, and iss), JWTs support custom claims that carry application-specific information. These claims enable you to embed authorization data, user metadata, and context directly in the token, reducing the need for additional database lookups on every request.
Types of custom claims:
The JWT specification defines three claim categories: registered claims (standardized by IANA), public claims (collision-resistant names, typically URIs), and private claims (agreed upon between parties). In practice, most applications use private claims with simple string keys.
{
"sub": "user_abc123",
"exp": 1700003600,
"iss": "https://auth.example.com",
"roles": ["admin", "editor"],
"tenant_id": "org_xyz",
"plan": "enterprise",
"feature_flags": ["beta_dashboard", "new_api"]
}
Naming conventions:
To avoid collisions with registered or public claims, use descriptive, application-specific names. Common approaches include namespace prefixes (https://example.com/roles), short prefixes (x-roles), or simply descriptive names that do not conflict with standard claims (tenant_id, plan). OpenID Connect uses the https:// URI format for extension claims to guarantee global uniqueness.
What to include in custom claims:
Good candidates for custom claims: user roles and permissions, tenant or organization identifiers, subscription tier, feature flags, and user preferences that affect API behavior. These are values that resource servers need on every request to make authorization decisions. By embedding them in the token, you avoid per-request database queries for this information.
What NOT to include:
Never embed sensitive data like passwords, social security numbers, credit card details, or API keys. Remember that JWT payloads are only base64url-encoded, not encrypted. Anyone who intercepts the token can decode the payload. Also avoid large data structures: arrays with hundreds of entries, nested objects, or base64-encoded files. These inflate token size and can exceed HTTP header limits.
Keeping claims synchronized:
Custom claims represent a snapshot of the user's state at token issuance time. If a user's role changes, existing tokens still carry the old role. Use short token lifetimes so claims refresh frequently. For critical changes (like permission revocation), implement a mechanism to force token reissuance, such as incrementing a version counter that servers check against the token's claims.
Use Case
A multi-tenant SaaS application includes tenant_id and roles claims in its JWTs so API endpoints can enforce tenant isolation and RBAC without querying the user database.