JWT Custom Claims: How to Add, Read, and Namespace Them Correctly
- What custom claims are and why you add them to a JWT
- How to namespace private claims to avoid collisions
- Adding roles, permissions, and org_id to a JWT payload
- How to read custom claims in JavaScript, Python, and server-side code
Table of Contents
Beyond the standard registered claims (sub, iss, exp), a JWT payload can carry any custom data your application needs. Roles, permissions, tenant IDs, feature flags — these are private claims. Here is how to add them correctly and read them in code.
Custom Claims vs Registered Claims
JWT claims fall into three tiers:
- Registered claims: The seven IANA-defined claims (sub, iss, aud, exp, iat, nbf, jti). Short names, broadly understood.
- Public claims: Names registered in the IANA JWT Claims Registry to avoid conflicts — like
email,name,picturefrom OpenID Connect. - Private (custom) claims: Your own application-specific data. Not registered anywhere. Only meaningful within your system.
Common custom claims in production apps: role, roles, permissions, org_id, tenant_id, plan, feature_flags. These let downstream services make authorization decisions without a database lookup.
How to Namespace Custom Claims (Required for Public Tokens)
If your JWT might ever be shared outside your system, use a URL namespace to avoid collisions with other claim names:
// Namespaced claims — safe for public or multi-party tokens
{
"sub": "user_12345",
"exp": 1735689600,
"https://myapp.com/role": "admin",
"https://myapp.com/org_id": "org_abc123",
"https://myapp.com/permissions": ["read:reports", "write:invoices"]
}
Auth0 specifically requires this pattern — if you add custom claims without a URL namespace, Auth0 will silently strip them from the token.
For internal systems where the JWT never leaves your infrastructure, short names are fine:
{
"sub": "user_12345",
"role": "admin",
"org_id": "org_abc123"
}
Sell Custom Apparel — We Handle Printing & Free Shipping
How to Add Custom Claims When Generating a JWT
// Node.js — jsonwebtoken
const jwt = require('jsonwebtoken');
const payload = {
sub: userId,
role: 'admin',
org_id: orgId,
permissions: ['read:reports', 'write:invoices']
};
const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '15m',
issuer: 'https://auth.myapp.com',
audience: 'https://api.myapp.com'
});
# Python — PyJWT
import jwt
import datetime
payload = {
'sub': user_id,
'role': 'admin',
'org_id': org_id,
'permissions': ['read:reports', 'write:invoices'],
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=15),
'iss': 'https://auth.myapp.com'
}
token = jwt.encode(payload, secret_key, algorithm='HS256')
How to Read Custom Claims After Decoding
Once you have decoded the JWT payload, custom claims are just regular JSON fields:
// JavaScript — after decoding or verifying
const payload = decodeJwt(token); // or jwt.verify() on server
const role = payload.role;
const orgId = payload.org_id;
const permissions = payload.permissions || [];
// Check permission
if (!permissions.includes('write:invoices')) {
return res.status(403).json({ error: 'Forbidden' });
}
For namespaced claims:
const role = payload['https://myapp.com/role'];
const permissions = payload['https://myapp.com/permissions'] || [];
Size warning: JWT payloads are sent with every request. Keep custom claims minimal — avoid including large arrays, nested objects, or data that changes frequently. A token over 4KB can cause issues with cookies and some HTTP headers.
Inspect Your JWT Custom Claims
Paste your token above to see all claims — including custom and namespaced ones — decoded and labeled instantly.
Open Free JWT DecoderFrequently Asked Questions
Can I put an array of roles in a JWT claim?
Yes. A claim value can be any valid JSON type — string, number, boolean, array, or object. Arrays of roles or permissions are common: "roles": ["admin", "editor"]. Just watch the total token size.
Can I update custom claims after a token is issued?
No — a JWT cannot be modified after issuance without invalidating the signature. If a user's role changes, you need to issue a new token (the next login or token refresh will pick up the new role).
Should I put sensitive data in custom claims?
No. JWT payloads are base64-encoded, not encrypted. Anyone who has the token can decode and read all claims. Never put passwords, SSNs, credit card numbers, or private business data in a JWT payload.

