Introduction to JWT Claims
JWT claims are pieces of information about an entity (typically a user or service) encoded within a JSON Web Token. The payload section of a JWT consists entirely of claims—structured data that tells the receiving application who the user is, when the token was issued, when it expires, and any custom information relevant to your application. Understanding claims is fundamental to working effectively with JWTs.
Claims are best thought of as assertions or statements about an entity. When you generate a JWT for a user, you're making claims like "this token belongs to user 123," "it was issued at this timestamp," "it expires in one hour," and potentially "this user has admin permissions." The receiving application trusts these claims based on the token's cryptographic signature, which verifies that the token came from a trusted issuer.
The beauty of JWT claims is their flexibility. While JWTs define a set of standard claims recognized across implementations, you can add any custom claims your application needs. This allows JWTs to carry all necessary information about a user or service without requiring additional database lookups. A single token can contain everything needed to authorize a request.
Registered Claims: The Standard Set
The JWT specification defines several registered claims that have standardized meanings. These are optional but recommended for interoperability and security. Understanding these standard claims helps you implement JWTs correctly and follow industry best practices.
The "iss" (issuer) claim identifies who created and signed the token. In a system where multiple services issue tokens, the issuer claim helps verify that a token came from an expected source. For example, your authentication service might set "iss":"auth.example.com" to indicate it issued the token. The receiving service can verify that the issuer is trusted before accepting the token.
The "sub" (subject) claim identifies the entity the token represents, typically the user ID. This is often the most important claim because most authorization logic depends on knowing who the request is from. The subject is usually a user ID, service account ID, or other unique identifier within your system. For example, "sub":"user_12345" identifies that this token represents user 12345.
The "aud" (audience) claim identifies the intended recipients of the token. This claim helps prevent token misuse—a token intended for Service A shouldn't work for Service B. For example, a token with "aud":"api.example.com" is only valid when presented to that specific API. A service should verify the audience claim contains its own identifier before accepting the token.
The "exp" (expiration time) claim specifies when the token becomes invalid, expressed as seconds since the Unix epoch (January 1, 1970). A token with "exp":1735689600 expires at that timestamp. After expiration, the token should be rejected regardless of other claims. This claim is critical for security—it limits the window of vulnerability if a token is stolen.
The "nbf" (not before) claim specifies when the token becomes valid. It's less commonly used than expiration but useful in scenarios where you want to create tokens that aren't immediately usable. For example, you might create tokens for scheduled tasks that shouldn't activate until a specific time.
The "iat" (issued at) claim records when the token was created, expressed as seconds since the Unix epoch. This helps track token age and can be useful for security monitoring. It often works in conjunction with token expiration to determine how long a token is valid.
The "jti" (JWT ID) claim provides a unique identifier for the token, useful for preventing token replay attacks. If you maintain a blacklist or whitelist of token IDs, you can revoke specific tokens or detect when the same token is used multiple times when it shouldn't be.
Custom Claims: Application-Specific Data
Beyond registered claims, you can add custom claims containing any information your application needs. Custom claims typically use namespaced identifiers to avoid conflicts. For example, instead of just "role":"admin", you might use "https://example.com/role":"admin" to clearly indicate this is a custom claim from your application.
Common custom claims include user roles and permissions. Rather than querying a database every time you need to know if a user is an admin or can perform a specific action, you can include this information directly in the token. This enables faster authorization decisions in distributed systems.
Email addresses, profile information, and other user data can be included as custom claims if needed. However, remember that JWT payloads are encoded but not encrypted—anyone who obtains the token can read these claims. Only include information that's safe to be exposed in the token.
Application-specific metadata can be embedded in claims. For example, an e-commerce platform might include "store_id":"store_789" to indicate which store the user is accessing, or a SaaS platform might include "organization_id":"org_456" to track which organization a request is for. This eliminates the need to store context in sessions or databases.
Private vs. Public Claims
The JWT specification distinguishes between public claims (registered and custom claims that are part of the standard or clearly named) and private claims used between parties who've agreed on their meaning. In practice, this distinction is less important than ensuring your claim names don't conflict with others.
If you're designing a system where multiple parties might generate or consume JWTs, use clearly namespaced custom claims. Following conventions like prefixing with your domain name (https://example.com/) or organization name prevents claim name collisions that could cause security issues.
Best Practices for Designing Claims
Keep claims minimal and focused on what's necessary for authorization and identification. Every claim you include increases the token size, which impacts transmission and processing. For systems handling high request volumes, token size directly affects performance.
Never include sensitive information like passwords, API keys, or credit card numbers in JWT claims. The payload isn't encrypted—assume any data in claims will be accessible to anyone with the token. Use token IDs and look up sensitive information from secure stores if needed.
Include claims that enable stateless authorization. The point of JWTs is avoiding database lookups for each request. Include enough information that the receiving service can make authorization decisions without querying a database. However, be careful with user data that might change frequently—if you include a permission that gets revoked, the user can still use the old token until it expires.
Document your custom claims thoroughly, especially in systems where multiple services handle tokens. Other developers need to understand what each claim means, what values are valid, and how the claim should be used for authorization decisions.
Token Size Considerations
JWT tokens grow with every claim you add. Large tokens impact performance in several ways: they consume more bandwidth when transmitted, take longer to verify, and slow down cookie-based storage. For high-traffic applications, consider the cumulative effect of claim sizes.
If you have many claims to include, consider using a claims reference approach: include a minimal set of claims in the JWT and store detailed information on the server with a reference ID. Include the reference ID in the token, and authorized services can look up additional details if needed. This approach requires some additional infrastructure but improves performance.
Accessing and Validating Claims
When a JWT is decoded, you get direct access to all claims as a JSON object. In JavaScript, after decoding a JWT with a library like jsonwebtoken, you might access claims like this:
const decoded = jwt.decode(token);
const userId = decoded.sub;
const userRole = decoded['https://example.com/role'];
const expiresAt = decoded.exp;
Always validate claims according to your application's rules. Verify that required claims are present, that their values are expected types, and that values are within acceptable ranges. Never assume a claim has a value—check for presence and type before using it.
For expiration and not-before times, compare them to the current server time. For audience and issuer claims, verify they match your expected values. For custom claims like roles, validate that the values are from your defined set of allowed roles.
Claims in Different JWT Use Cases
For browser-based applications, keep claims minimal since the entire token is transmitted with each request. Include user ID, role if needed for frontend logic, and perhaps the organization ID. Save detailed user information for server-side lookups or separate API calls.
For service-to-service authentication, include service identifiers, permission scopes, and any routing information needed. Services often use more claims than user-facing applications because they can efficiently process and validate them without frontend constraints.
For long-lived refresh tokens, include minimal claims—primarily the subject and issuer. Refresh tokens shouldn't contain user permissions because they're stored longer and represent a higher security risk. Use them only to request new short-lived access tokens.
Conclusion
JWT claims are the foundation of token-based authentication and authorization. Registered claims like "sub," "exp," and "iat" provide standardized ways to express common information, while custom claims enable application-specific data. The key to effective JWT usage is thoughtful claim design: include only what's necessary, never store sensitive information, and always validate claims when processing tokens. Well-designed claims enable stateless, scalable authentication systems; poorly designed claims create performance bottlenecks and security vulnerabilities.


