Custom claims
Custom claims let you propagate application metadata (e.g. role, company_id, wallet_address) inside the session JWT instead of joining a separate user table on every authenticated request.
Claims are gated by a per-application whitelist schema configured in the dashboard (see “Define schema” below). Anything not in the whitelist is rejected with CUSTOM_CLAIM_NOT_ALLOWED.
Limits
- Up to 10 keys per token.
- Total payload ≤ 2 KB of JSON.
- Scalar values only:
string,number,boolean. No nested objects/arrays.
Exceeding any limit returns CUSTOM_CLAIMS_TOO_LARGE or CUSTOM_CLAIM_NOT_ALLOWED.
Define the schema (dashboard)
Dashboard → Applications → [your app] → Advanced → Custom claims schema.
Today the schema is read-only in the UI; updates go through PATCH /v1/applications/:id with custom_claims_schema in the body. Editor UI is on the F0 follow-up roadmap.
Example schema:
{
"role": { "type": "string" },
"company_id": { "type": "number" },
"wallet_address": { "type": "string" }
}
Pass claims at login
import { TryMellon } from '@trymellon/js';
const client = TryMellon.create({ clientId: '…' }).value!;
const result = await client.signIn({
externalUserId: 'user_123',
customClaims: {
role: 'admin',
company_id: 42,
},
});
customClaims is also accepted on signUp and enroll.
Read claims on the backend
Claims live under the https://trymellon.dev/claims namespace in the JWT (per the OIDC custom-claim convention to avoid collisions).
import { jwtVerify, createRemoteJWKSet } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://api.trymellonauth.com/.well-known/jwks.json'),
);
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://api.trymellonauth.com',
});
const claims = payload['https://trymellon.dev/claims'] as
| { role?: string; company_id?: number; wallet_address?: string }
| undefined;
console.log(claims?.role); // 'admin'
For Go and Python examples see Reading custom claims.
Use cases
| Claim | Why in JWT instead of a DB lookup |
|---|---|
role | Authorization decisions in middleware — no DB hit per request. |
company_id | Multi-tenant routing without an extra SELECT. |
wallet_address | Web3 dApps verifying intent on-chain — already proven during login. |
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
CUSTOM_CLAIM_NOT_ALLOWED | Key not in the application’s whitelist. | Add it to custom_claims_schema in the dashboard. |
CUSTOM_CLAIMS_TOO_LARGE | > 10 keys or > 2 KB. | Drop to references (e.g. company_id instead of company_name + company_country + …). |
| Claim missing in JWT | Schema rejects the value’s type. | Make sure runtime type matches the schema declaration. |