What this is about
A token is a JSON document with claims: named entries like sub (who), exp (until when) or realm_access (which roles). CIAS reads the claims from the exchanged token and puts the results into the RequestContext. CDMS reads from there, and only from there.
From the token to the RequestContext
flowchart LR
subgraph T["Exchanged token"]
sub["sub"]
name["name / preferred_username"]
rr["realm_access.roles"]
cr["resource_access.(client).roles"]
grp["groups"]
org["organization"]
ten["tenant"]
at["allowedTenants"]
oth["projects, …"]
end
subgraph R["RequestContext"]
uid["userId"]
un["userName"]
err["effectiveUserRealmRoles"]
eur["effectiveUserRoles"]
eug["effectiveUserGroups"]
ut["userTenant"]
alt["allowedTenants"]
eua["effectiveUserAttributes"]
end
sub --> uid
name --> un
rr --> err
cr --> eur
org -. "roles in the tenant replace" .-> eur
grp --> eug
org --> ut
ten --> ut
at --> alt
org --> alt
oth --> eua
The table
| Claim in the token | Setting | Ends up in | Note |
|---|---|---|---|
sub | claims.user-id | userId | the ID of the account in Keycloak |
name, otherwise preferred_username | claims.user-name | userName | the first one that has a value |
realm_access.roles | claims.realm-roles | effectiveUserRealmRoles | always apply, in every tenant |
resource_access.<client>.roles | claims.client-roles | effectiveUserRoles | only its own client. Roles in the tenant can replace them |
groups | claims.groups | effectiveUserGroups | the groups, as Keycloak writes them into the token |
organization | organization.claim | userTenant, allowedTenants, roles in the tenant | the alias of an organization is the tenant key |
tenant | claims.tenant-attribute | userTenant | the static way to the tenant, first value |
allowedTenants | claims.allowed-tenants-attribute | allowedTenants | list, split at commas |
| all others | – | effectiveUserAttributes | as a list of strings |
All settings are under codamai.cias.token. If you rename a claim, you set the new name here.
Each variant
When: for every token with a person
sub becomes the userId. This is what CDMS stores in _userId and in the history. The name comes from name or, if that is missing, from preferred_username. A service account has no name and is therefore called service-account-<client>.
When: for every token
Realm roles from realm_access.roles go unchanged to effectiveUserRealmRoles. Business roles come from resource_access.<client>.roles, and only for the own client of CIAS. The roles of other clients in the token are not read.
Result: How roles in the tenant replace the business roles: Effective roles.
When: when Keycloak writes groups into the token
The groups claim becomes effectiveUserGroups. The roles from groups are already in their places in the token anyway; the groups themselves are only there for information.
When: for dynamic tenants
The organization claim says which organizations the person is a member of and which roles they have there. CIAS understands three forms: a list of aliases, an object with the alias as key, or a list of objects with the alias under alias or name. Entries without an alias are dropped. The alias is the tenant key.
Result: Which tenant results from this: Determine the tenant of a request.
When: in operating mode MULTI
allowedTenants in the RequestContext is a list in a fixed order: first the person's own tenant, then all organizations of the person, then the values of the allowedTenants attribute, split at commas. In SINGLE the list is empty.
Result: The list decides the tenant switch via header.
When: for every other claim
Every claim that is not in the table above and is not a protocol claim becomes an attribute, such as projects. The value always becomes a list of strings: a single value becomes a list with one entry. Objects and nested lists are skipped. If an attribute is registered per tenant, the value from CIAS replaces the one from the token.
Result: What CDMS uses the attributes for: Attribute filter.
When: claims that belong to the protocol
These claims do not become attributes: iss, sub, aud, exp, nbf, iat, jti, typ, azp, nonce, auth_time, acr, amr, sid, session_state, scope, client_id and similar ones, the profile claims like name, email, preferred_username, locale, plus realm_access, resource_access, allowed-origins, groups and organization.
Result: So an email address in the token is not an attribute for a filter.
An example
{
"sub": "7f3c…",
"preferred_username": "anna",
"name": "Anna Berg",
"realm_access": { "roles": ["offline_access"] },
"resource_access": { "cdms-backend": { "roles": ["hr-employee-read"] } },
"organization": { "nordbau": { "id": "a1b2…" } },
"projects": ["alpha", "beta"],
"email": "anna@nordbau.example"
}userId = 7f3c…
userName = Anna Berg
userTenant = nordbau
allowedTenants = [nordbau]
effectiveUserRealmRoles = [offline_access]
effectiveUserRoles = [hr-employee-read]
effectiveUserAttributes = { projects: [alpha, beta] }email is a protocol claim and does not become an attribute. projects does.