
JWT Custom Claims: Public, Private, and Custom | JWT From the Spec Up
Understand the distinctions between public, private, and custom claims in JSON Web Tokens, including IANA registry usage and namespacing best practices.
Public, Private, and Custom Claims
In the architecture of JSON Web Tokens (JWTs), developers often conflate the security of a claim with its type. This is a category error. The classification of a JWT claim—public, private, or custom—does not dictate whether it is encrypted or signed. It dictates how a verifier interprets the claim’s origin and manages the risk of key collision.
When you design a token, you are defining a contract. The type of claim tells the verifier whether to trust a global standard, an internal agreement, or a namespaced extension. Misunderstanding these distinctions leads to security vulnerabilities where one service’s custom data accidentally overwrites another’s critical identifier, or where a verifier fails to recognize a new, standardized claim.
The IANA Registry and Public Claims
A "public" claim is one that has been registered with the Internet Assigned Numbers Authority (IANA) in the JSON Web Token registry here. The existence of this registry is the mechanism that allows disparate systems to communicate without pre-negotiation with the iana registry.
RFC 7519 defines specific reserved claims that are public by default. These include iss (issuer), sub (subject), aud (audience), exp (expiration time), nbf (not before), iat (issued at), and jti (JWT ID).
The mechanism here is standardized interpretation. When a verifier encounters exp, it does not need to ask the issuer what that field means. It knows, by virtue of the IANA registration, that exp is a NumericDate representing the time after which the token must not be accepted. If the current time exceeds exp, the token is rejected.
This standardization reduces the attack surface. Because the key name is globally unique and the semantics are fixed, a malicious actor cannot inject a claim named exp with a different meaning to confuse the verifier. The verifier trusts the key name because it is part of the public specification.
However, relying solely on public claims limits flexibility. You cannot register a new claim for every specific business logic requirement in your application. While public claims are standardized, jwt custom claims require careful handling to avoid collisions. This is where private and custom claims come into play.
Private Claims: The Two-Party Contract
A "private" claim is an agreement between two parties who share information. There is no registry entry. There is no global standard. The security guarantee comes from the fact that the claim is never exposed to third parties. Unlike public claims which are globally registered, private claims rely on local agreement.
Consider a scenario where Service A issues a JWT to Service B. Both services agree that the claim internal_user_tier indicates the user’s subscription level. Neither service exposes this claim to the frontend or to other microservices.
The mechanism here is isolated trust. Because the claim is not shared, there is no risk of collision with other systems. If Service A decides to rename internal_user_tier to subscription_level, it only needs to update Service B. There is no need to coordinate with the broader ecosystem.
Private claims are ideal for internal microservice communication where the trust boundary is well-defined. However, they are dangerous if used across organizational boundaries. If you send a private claim to a third-party OAuth provider, that provider may ignore it, misinterpret it, or, worse, a future version of their library might introduce a conflicting claim with the same name.
Custom Claims and the Namespace Problem
The most common source of confusion in JWT design is the "custom" claim. A custom claim is any claim that is not registered in the IANA registry and is not part of a private two-party agreement. It is often used to extend the token with application-specific data, such as role, permissions, or department.
The problem arises when you use simple, non-namespaced keys like role, which might conflict with entries in the iana registry. If the IANA were to register a role claim in the future, and you had been using role as a custom claim for years, your tokens could suddenly become incompatible with the new standard, or worse, your custom data could be overwritten by a standard claim.
RFC 7519 Section 4.2 explicitly addresses this:
"Claim Names can be defined at will by those using JWTs. However, in order to prevent collisions, any new Claim Name should either be registered in the IANA 'JSON Web Token Claims' registry ... or be a Public Name: a value that contains a Collision-Resistant Namespace."
The mechanism for preventing collision is namespacing. Instead of using a flat key like role, you should use a URI as the key. For example:
{
"https://mycompany.com/claims/role": "admin",
"https://mycompany.com/claims/dept": "engineering"
}By using a fully qualified domain name (FQDN) as the key, you guarantee uniqueness. Even if the IANA later registers a claim named role, your claim https://mycompany.com/claims/role remains distinct. The verifier treats the entire URI string as the key, ensuring no ambiguity.
This approach also solves the problem of multiple organizations sharing the same token infrastructure through effective namespacing. If Organization A and Organization B both use JWTs, they can both use https://org-a.com/claims and https://org-b.com/claims without interfering with each other.
Best Practices for Claim Design
To ensure secure and maintainable JWT designs, follow these mechanisms:
- Use Reserved Claims for Standard Functions: Always use
exp,iat,iss, andsubfor their intended purposes. Do not repurpose them. - Namespacing for Custom Data: Never use simple strings like
roleoruser_idfor custom data. Use a URI namespace:https://<your-domain>/claims/<key>. - Limit Payload Size: While not directly related to claim types, remember that JWTs are often base64url-encoded and placed in headers or URLs. Keep custom claims minimal. If you need large amounts of data, store the reference in the JWT and fetch the details from a database.
- Document Custom/Private Claims: If you use private or custom claims, document them thoroughly. Since there is no registry, the only source of truth is your internal documentation.
Conclusion
The distinction between public, private, and custom claims is about provenance and collision resistance. Public claims leverage the IANA registry for global interoperability. Private claims rely on isolated trust for internal efficiency. Custom claims, when properly namespaced, allow for scalable extension without breaking existing systems.
By treating claim types as a mechanism for managing trust and uniqueness, rather than just a categorization of data, you build JWTs that are resilient to evolution and secure against collision attacks.
Related posts
The Seven Registered Claims: iss, sub, aud, exp, nbf, iat, jti
A technical breakdown of the seven standard JWT claims: iss, sub, aud, exp, nbf, iat, and jti, explaining their roles in authentication and authorization.
Anatomy of a JWT: Header, Payload, and Signature
A technical breakdown of JSON Web Tokens (JWT) structure, explaining the header, payload, and signature components as defined in RFC 7519.
Reactive Security: WebFlux & JwtAuthenticationToken
Explore WebFlux security patterns using ReactiveSecurityContextHolder and JwtAuthenticationToken for non-blocking authentication.