What this is about
A role can be attached to a person in three ways. Two questions decide which way, and they are independent of each other:
- Which level in Keycloak? If the role has no client, it is a realm role. Otherwise it is a client role of the client it belongs to.
- Which reach? A grant with scope
PLATFORMis attached directly to the account. A grant with scopeTENANTis attached to the tenant’s organization.
The three levels
| Realm role | Client role, global | Client role in the organization | |
|---|---|---|---|
| Granted to | the account, realm-wide | the account, for one client | the membership in an organization |
| In the token under | realm_access.roles | resource_access.<client>.roles | organization.<alias>.resource_access.<client>.roles |
| Applies | always and everywhere | everywhere the person has no dynamic tenant with its own roles | only in the tenant of this organization |
| Typical roles | platform-admin, user, the two switch roles, declaration-reader | roles in static tenants | roles in dynamic tenants, for example tenant-admin |
| Who reads it | platform checks: administrator, switch, reading the declaration | the module of this client | the module of this client, when the request runs in this tenant |
What it looks like in the token
{
"realm_access": {
"roles": ["user", "allowed-tenant-context-switch"]
},
"resource_access": {
"cdms-backend": { "roles": ["report-read"] }
},
"organization": {
"nordbau": {
"id": "b7e0…",
"resource_access": {
"cdms-backend": { "roles": ["order-edit", "tenant-admin"] }
}
}
}
}Request runs in nordbau, client cdms-backend:
Realm roles: user, allowed-tenant-context-switch
Business roles: order-edit, tenant-admin
(report-read does NOT apply here)report-read is missing in nordbau because the organization carries its own roles. These replace the global client roles. They are not mixed with them. If an organization carries no roles at all, the global ones still apply. The full rule is described in Effective roles: global or in the tenant.
How CIAS enters a role in Keycloak
| Role | Scope | What CIAS does in Keycloak |
|---|---|---|
| Realm role | PLATFORM | enters the role directly on the account |
| Client role | PLATFORM | enters the client role directly on the account |
| Client role | TENANT | makes the person a member of a group in the organization that is named <client>:<role> and carries the role |
| Realm role | TENANT | makes the person a member of a group in the organization that is named after the role |
Keycloak’s organizations have no roles of their own, only groups. So a tenant role is always: a group in the organization, the role on this group, the person as a member. The name <client>:<role> prevents two modules with the same role name from sharing one group.
A nice side effect: a role through an organization group is never among the person’s direct realm roles. So an organization cannot extend the global roles.
Who reads which level
- Realm roles are read by CIAS itself: Is the person a platform administrator? May the person switch the tenant or the user? No tenant can add realm roles. Otherwise, whoever manages a tenant could give themselves platform permissions.
- Client roles are read by the module of the client, so each application reads its own. The filter chain skips the roles of other clients, even if they are in the token.
The trap with global client roles
A client role with scope PLATFORM ends up in resource_access. But the normal roles of a static tenant are there too. For a person in a dynamic tenant whose organization carries roles, exactly this part is replaced. The grant would be in the token but would never apply.
So CIAS refuses to grant a client role with scope PLATFORM to a person whose home tenant has an organization: 403 cias.authorization.denied. For the same reason, a module may not register platform roles.