Going deep into OIDC claims and the user profile in Keycloak: standard profile, email, address, and phone claims, creating custom claims via user attributes and protocol mappers, and strategies for balancing the ID token and the UserInfo endpoint.

In episode 9 you saw the ID token containing claims like name and email. Episode 10 opens the hood: where those claims come from, what standard claims exist, how to add custom claims via user attributes and protocol mappers, and how to choose a strategy — load everything into the token, or fetch it from the UserInfo endpoint.
OIDC defines a set of standard claims grouped by category:
name, given_name, family_name, picture, and others.email and email_verified.address containing formatted, street_address, locality, region, postal_code, and country.phone_number and phone_number_verified.These claims are mapped from Keycloak's user data by built-in protocol mappers already attached to the profile, email, address, and phone client scopes.
User data in Keycloak is stored in two layers:
department or employee_id.Custom attributes are added on the user page, then elevated into claims via a mapper:
{
"sub": "9c4d5e6f-...",
"preferred_username": "budi",
"department": "engineering",
"employee_id": "EMP-1042"
}Above, department and employee_id are custom claims resulting from user attribute mapping. Applications can then read these claims for business logic — e.g. deciding which menu to show.
A protocol mapper is the bridge between Keycloak data and tokens. Keycloak provides several mapper types:
| Mapper type | Function | Example result |
|---|---|---|
| User Attribute | Copies a user attribute to a claim | department |
| User Property | Copies a built-in user property | locale, createdTimestamp |
| Client ID | Writes the client identity | client_id as a claim |
| Audience | Adds to the token's aud | additional audience |
| Role | Maps roles to claims | roles in realm_access |
| Group | Maps user groups | group membership |
| Hardcoded claim | Inserts a fixed value | env: production |
| JavaScript | Custom mapping logic | free-form transformation |
Mappers can be attached to a client scope (applies to all clients using that scope) or directly to a client (only that client). Choose the client scope when many clients need the same claim.
Two mappers often overlooked despite being practical:
"tier": "gold" for all tokens from one client.firstName and lastName into display_name, or reformatting a number.const fullName = user.getFirstName() + " " + user.getLastName();
token.setClaim("display_name", fullName);Use the JavaScript mapper carefully and only when the built-in mappers aren't enough — a broken script can slow down token issuance or introduce bugs that are hard to trace.
Claim mapping isn't only about attributes. Four mapping patterns are common in production:
aud value so the token can be used across more than one resource server, or conversely narrows the audience.Three different claim paths, with different consequences:
| Aspect | ID Token | Access Token | UserInfo |
|---|---|---|---|
| Content | User identity | Authorization and roles | Profile per scope |
| Consumer | Client app | Resource server | Client app |
| Claim richness | Compact | Depends on API needs | Can be fuller |
| Size effect | Grows with each claim | Grows with every request | Doesn't touch tokens |
To read a profile via UserInfo, the application simply calls its endpoint with the access token:
curl -s -H "Authorization: Bearer eyJhbGciOi..." \
"https://kc.example.com/realms/my-realm/protocol/openid-connect/userinfo"protocol/openid-connect/userinfo returns the claims allowed by the scopes the user approved — the opposite of the ID token, whose contents are locked in once the token is issued.
Choosing where to place claims is a design decision with three considerations:
Size is also a cost: bloated tokens slow requests and waste memory. As a rule of thumb, load into the token only what's truly used per request; the rest goes through the UserInfo endpoint.
Warning
Claims inside a token cannot be withdrawn before the token expires. If a user is fired or their role changes, still-valid old tokens keep carrying the old claims. That's why you shouldn't put changeable security decisions into the token; put them on the resource server, which can check the latest state on every request.
Episode 10 covered OIDC claims and the user profile: standard claims from profile to phone, user attributes as a source of custom data, the protocol mapper types including hardcoded and JavaScript mappers, claims mapping patterns, and strategies for balancing the ID token, access token, and UserInfo endpoint.
Key takeaways:
In the next episode (episode 11), you'll learn session management — how Keycloak tracks login sessions across many applications at once, how single logout works, and how to monitor and revoke sessions from the admin console.