JWT Claims are the core data that a JWT token carries. These claims convey specific statements about a subject (e.g., a user), and are encoded in the Payload section of a JWT.
4.1. JWT Claims Set
The JWT Claims Set is a JSON object containing a collection of name/value pairs called claims. These claims assert specific facts about a subject, like:
- Who issued the token
- When it expires
- Who it’s intended for
- Any additional metadata (like user role, email, etc.)
Key Characteristics
- Unique Claim Names: Each claim name within a JWT Claims Set must be unique. If duplicate names are present, the JWT parser must reject the token, or it must use a JSON parser that keeps only the last occurrence of a name.
- Context-Dependent Validity: Which claims are required in a JWT depends entirely on the application or context. There is no universal set of required claims.
- Unknown Claims Must Be Ignored: If a JWT contains claims not understood by the system processing it, those claims must be ignored, unless the context dictates otherwise.
Claim Name Types
There are three categories of claim names in JWT:
- Registered Claim Names
Predefined, commonly used claims that improve interoperability. - Public Claim Names
Custom claims with globally unique names to avoid conflicts. - Private Claim Names
Custom claims agreed upon between systems, which may collide unless carefully managed.
4.1 Registered Claim Names
These are standardized claim names defined in the IANA “JSON Web Token Claims” registry. They are not mandatory but are recommended for interoperability between different systems.
4.1.1 "iss" (Issuer)
- Identifies who issued the JWT.
- Typically a StringOrURI (e.g.,
"https://auth.mycompany.com"). - This is application-specific and optional.
4.1.2 "sub" (Subject)
- Identifies the subject of the JWT, i.e., who or what the token is about.
- Can be a username, user ID, email, etc.
- Must be unique within the context of the issuer.
- Also a StringOrURI.
- Optional, but widely used in identity systems.
4.1.3 "aud" (Audience)
- Identifies the intended recipients of the JWT.
- Each recipient must verify that it is included in the
"aud"value. - The value can be:
- A single string (for one audience).
- An array of strings (for multiple audiences).
- Optional but must be validated if present.
4.1.4 "exp" (Expiration Time)
- Indicates the time after which the token is no longer valid.
- Must be a NumericDate (i.e., Unix timestamp in seconds).
- Systems must reject tokens where
current time > exp. - Implementations may allow clock skew (typically 1–5 minutes).
- Optional, but strongly recommended for security.
4.1.5 "nbf" (Not Before)
- Specifies the time before which the JWT must not be accepted.
- Also a NumericDate.
- Used to prevent tokens from being used prematurely.
- Allows limited future-valid tokens (e.g., scheduled access).
- Optional and may be used with
"exp"to define a valid time window.
4.1.6 "iat" (Issued At)
- Indicates when the JWT was issued.
- Helps calculate the token age.
- Also a NumericDate.
- Useful for detecting replay attacks or validating token freshness.
- Optional.
4.1.7 "jti" (JWT ID)
- Provides a unique identifier for the token.
- Used to detect and prevent replay attacks.
- Should be a globally unique string (e.g., UUID).
- Must be generated in such a way that collision is practically impossible, even across different issuers.
- Optional, but important in stateless authentication to enable blacklisting.
4.2 Public Claim Names
Developers or vendors may define their own custom claim names beyond the registered ones. To avoid name collisions, such custom claims should use a collision-resistant naming scheme. Examples include:
- Fully qualified domain names (e.g.,
"https://example.com/user_level") - UUID-based namespaces
- OIDs (Object Identifiers)
Such claims are called Public Claim Names, and it’s recommended that they be registered with IANA if they are going to be reused across systems.
4.3 Private Claim Names
Private Claim Names are used internally between two parties. These names are:
- Not registered in IANA
- Not globally unique
- Agreed upon by sender and receiver
For example:
{
"user_id": "789",
"internal_code": "ABC123"
}
While flexible and convenient, private claims may conflict with other claim names if used across systems. Therefore, they should be used only in isolated or trusted systems.
Summary of Claim Name Categories
| Type | Purpose | Naming Convention | Risk of Collision |
|---|---|---|---|
| Registered Claims | Standard, well-known claims | Short, predefined names | Low |
| Public Claims | Custom claims with global uniqueness | Namespaced (e.g., domain URI) | Low |
| Private Claims | Internal, custom claims shared by trusted parties | Freely chosen | High |
Example JWT Payload with Claims
{
"iss": "https://auth.example.com",
"sub": "1234567890",
"aud": ["my-api", "my-service"],
"exp": 1718038200,
"nbf": 1718034600,
"iat": 1718034600,
"jti": "f1f8d9a8-80b8-4f68-b191-73d6f3f8d2e4",
"role": "admin",
"https://example.com/is_premium_user": true
}
- The first 7 claims are registered.
"role"is a private claim."https://example.com/is_premium_user"is a public claim using a namespaced URI.
