How should custom claims be named in a JWT so they do not collide with other parties' claims?
answer
- Three classes, one axis: collision risk
- Public means the name, not the value
- Domain ownership is a free namespace
- Two issuers, one claim name, one incident
- Never repurpose a registered name
basics
~20 sUse a collision-resistant name — typically a URI in a namespace you control — or a name registered in the IANA JWT Claims registry. Short private names like roles are fine only inside a closed system where producer and consumer agree.
solid answer
~50 sJWT names fall into three classes. **Registered** claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`) are in the IANA JSON Web Token Claims registry with fixed meanings. **Public** claims are names anyone may use safely: either registered in that same registry, or made *collision-resistant*, which in practice means a URI under a domain you control, such as `https://example.com/claims/roles`. **Private** claims are short names agreed bilaterally between a producer and a consumer — `roles`, `tenant` — with no protection against another party using the same name for something else. The rule of thumb: private names are acceptable when a single issuer and a closed set of your own services are the only parties involved; the moment a token crosses an organizational boundary, or a service consumes tokens from more than one issuer, namespace them. And never redefine a registered name with different semantics — every generic validator assumes the standard meaning.
code
json · 6 lines{
"iss": "https://auth.example.com",
"sub": "user-42",
"https://example.com/claims/roles": ["billing-admin"],
"https://example.com/claims/tenant": "acme"
}go deeper
Know that beyond the registered claims you may add your own, and that reusing a registered name such as sub or exp for something else is never acceptable.
Explain the registered, public and private classes and why a URI you control makes a name collision-resistant.
Give the concrete failure — two issuers, one claim name, an escalation no signature check catches — and weigh namespacing cost against the chance of a foreign token reaching the verifier.
Own the claim vocabulary as a platform contract: who may add claims, how a name is migrated across issuers and consumers, and how to keep tokens from becoming an unversioned shared schema.
## Three classes of claim name The JWT specification splits claim names into registered, public and private, and the distinction is entirely about *collision risk*, not about confidentiality. That confusion is worth naming immediately: a "public claim" is not a claim that is safe to disclose — every claim in a signed token is readable — it is a claim whose *name* is safe to use because nobody else will use it to mean something different. - **Registered** — the small set in the IANA JSON Web Token Claims registry, whose semantics are fixed by specification. - **Public** — names either registered in that registry or defined to be collision-resistant. - **Private** — everything else: names producer and consumer agree on privately, with collisions their own problem. ## What collision-resistant means The practical mechanism is a URI in a namespace you control, and it works because domain ownership is already a global registry. A claim named `https://example.com/claims/roles` cannot be accidentally reused with different semantics by another organization, because only one party controls `example.com`. This is why identity providers that let customers add custom claims force a namespace prefix: they must guarantee that a customer's claim can never shadow or be confused with a claim the provider itself defines. ## Why collisions actually hurt The damage is not aesthetic. Consider a service that accepts tokens from its own issuer and a partner's, and reads a `roles` claim. Your issuer emits `["billing-admin"]`; the partner emits `["admin"]` meaning admin of *their* console. Both tokens verify against their respective issuers, and the same code reads the same claim name to different effect — a privilege escalation that no signature check can catch, because both tokens are perfectly authentic. Namespacing turns that into two distinct claim names, and the partner's simply is not read. A quieter version bites during migrations: a name that was private and unambiguous becomes ambiguous the day a second issuer is introduced, long after the code that reads it was written and forgotten. ## When private names are fine Namespacing has a cost — long names bloat a payload that travels in every request header, and they are awkward to read in code and logs. Inside a closed system with one issuer and services you own, a short private name is a reasonable and common choice. The decision hinges on a single question: *can a token from a party you do not control ever reach this verifier?* If yes, namespace. If the answer is "not today", weigh how likely that changes, because retrofitting a claim name across issuers and consumers is an awkward dual-emit migration. ## Rules that hold either way - **Never repurpose a registered name.** Using `sub` for something other than the principal, or `exp` for anything but expiry, breaks every library that validates generically. This is the one genuinely non-negotiable rule. - **Claim names are case-sensitive.** `Roles` and `roles` are different claims; relying on a JSON library's leniency invites bugs. - **The claims set is a JSON object, so duplicate member names are trouble.** Parsers disagree about whether the first or last wins, which is precisely the ambiguity an attacker probes for. Emit each name once. - **Absent is not empty.** Consumers must distinguish a missing claim from a claim present with an empty value; "missing means no permissions" needs to be an explicit decision, not an accident of deserialization. ## Keep the vocabulary small A final piece of judgment that separates senior answers: every claim added to a token is a schema shared by every service that reads it, and it can only be changed by coordinating across all of them. Custom claims tend to accumulate — a flag added for one feature is still there years later, in every request, readable by every client. Namespacing solves *collisions*; it does not solve *sprawl*. The stronger discipline is to carry identity and coarse authorization in the token and let services look up the rest behind their own checks.
- Does "public claim" mean the claim is safe to disclose?No — that is a common misreading. Every claim in a signed token is readable by anyone holding it. "Public" describes the *name*: one that is registered or collision-resistant, so any party may use it without another party meaning something different by it. Disclosure is a separate decision about the value.
- What breaks if two issuers both emit a claim named roles?A verifier accepting both reads one claim name with two different vocabularies, so a role meaningful in one issuer's world is honoured in yours. Both tokens are authentic and both signatures verify, so no cryptographic check catches it. Namespaced names make the foreign claim simply unreadable to your code.
- How would you migrate a private claim name to a namespaced one?Emit both for a period: issuers add the namespaced claim while keeping the old one, consumers are updated to prefer the namespaced name and fall back, and once every consumer is confirmed on the new name the old one is dropped. The window must exceed the maximum token lifetime, since old tokens stay in circulation.
saying these in an interview costs you the question
- Thinks public claims are ones safe to expose
- Reuses a registered name with custom meaning
- Assumes claim names are case-insensitive
- Adds short custom names to tokens shared with partners
- Emits the same claim name twice in one payload