Custom Claims
Custom claims let an admin attach arbitrary name / value pairs to a user. When the custom_claims scope is granted, those pairs are merged into the user’s ID token, access token, and UserInfo response. Use them to carry application-specific attributes (tenant, tier, department, entitlements) that Autentico does not model natively.
Custom claims in tokens
Section titled “Custom claims in tokens”When the custom_claims scope is requested and granted, every custom claim assigned to the user is included in:
- Access tokens – as string values
- ID tokens – as string values
- UserInfo responses – as string fields
Example token claims:
{ "sub": "user-123", "tier": "gold", "region": "eu", "scope": "openid custom_claims"}Values are strings
Section titled “Values are strings”Every custom claim value is emitted as a JSON string – "tier": "gold", never "tier": 3 or "active": true. Typed values (numbers, booleans, arrays) are not supported in this version.
Reserved names
Section titled “Reserved names”A custom claim may not shadow a registered claim. Any name in the IANA “JSON Web Token Claims” registry is rejected on write with 400 invalid_request — this covers the RFC 7519 claims (iss, sub, exp, …), the OIDC standard claims (name, email, address, …), and security- and authorization-relevant claims Autentico does not itself emit (cnf, roles, entitlements, act, authorization_details, verified_claims, …). Transport-specific entries (SIP, CDNI) are not reserved.
As a second layer, even if a reserved name is inserted directly into the database, the token builder never lets a custom claim overwrite a claim it has already set.
Naming
Section titled “Naming”Claim names must match ^[a-zA-Z_][a-zA-Z0-9_.:/-]*$ (bare names like tier, or collision-resistant namespaced names like https://app.example.com/tier). OIDC Core §5.1.2 recommends collision-resistant or private claim names for non-standard claims; bare names are acceptable when Autentico is used within a private subsystem.
Admin API endpoints
Section titled “Admin API endpoints”All custom claim endpoints require admin authentication.
| Method | Endpoint | Description |
|---|---|---|
GET |
/admin/api/users/{id}/claims |
List a user’s custom claims |
POST |
/admin/api/users/{id}/claims |
Create or update one custom claim |
DELETE |
/admin/api/users/{id}/claims/{name...} |
Remove one custom claim |
List a user’s claims
Section titled “List a user’s claims”curl https://auth.example.com/admin/api/users/$USER_ID/claims \ -H "Authorization: Bearer $ADMIN_TOKEN"Response:
{ "data": [ { "name": "region", "value": "eu", "updated_at": "2026-01-15T10:00:00Z" }, { "name": "tier", "value": "gold", "updated_at": "2026-01-15T10:00:00Z" } ]}Create or update a claim
Section titled “Create or update a claim”curl -X POST https://auth.example.com/admin/api/users/$USER_ID/claims \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "tier", "value": "gold"}'| Field | Required | Validation |
|---|---|---|
name |
Yes | 1–128 characters, matches the name pattern, not a reserved name |
value |
No | Up to 4096 characters |
The operation is an upsert: posting an existing name again replaces its value. Returns 201 Created on success, 404 Not Found if the user does not exist, 400 invalid_request for a reserved or malformed name.
Delete a claim
Section titled “Delete a claim”curl -X DELETE https://auth.example.com/admin/api/users/$USER_ID/claims/tier \ -H "Authorization: Bearer $ADMIN_TOKEN"The name is the rest of the path. For a namespaced claim name, percent-encode the slashes as %2F (e.g. .../claims/https:%2F%2Fapp.example.com%2Ftier) — sent raw, the // is path-cleaned and the request is redirected. Returns 404 Not Found if the claim does not exist.
Database table
Section titled “Database table”user_claims table
Section titled “user_claims table”| Column | Type | Description |
|---|---|---|
user_id |
TEXT | Foreign key to users.id |
claim_name |
TEXT | The claim name |
claim_value |
TEXT | The claim value (string) |
created_at |
DATETIME | When the claim was created |
updated_at |
DATETIME | Last update timestamp |
The primary key is (user_id, claim_name) – one row per pair. Deleting a user cascades to remove all of that user’s custom claims.