Keycloak roles and groups are not just for access control; they’re a sophisticated, hierarchical authorization system that can model complex relationships and policies.
Let’s see this in action. Imagine you have a multi-tenant application where each tenant has its own set of users, and within each tenant, users have different responsibilities.
{
"realm": "my-app",
"clients": [
{
"clientId": "my-app-frontend",
"name": "My App Frontend",
"baseUrl": "http://localhost:3000",
"publicClient": true,
"webOrigins": [
"http://localhost:3000"
],
"protocol": "openid-connect",
"consentRequired": false,
"enabled": true,
"attributes": {}
}
],
"roles": {
"realm": [
{
"name": "tenant-admin",
"description": "Global administrator for all tenants",
"composite": true,
"composites": {
"realm": [],
"client": {
"my-app-frontend": [
"view-tenants",
"manage-users",
"manage-roles"
]
}
}
}
],
"client": {
"my-app-frontend": [
{
"name": "view-tenants",
"description": "Allows viewing tenant list",
"composite": false,
"clientRole": true
},
{
"name": "manage-users",
"description": "Allows managing users within a tenant",
"composite": false,
"clientRole": true
},
{
"name": "manage-roles",
"description": "Allows managing roles within a tenant",
"composite": false,
"clientRole": true
}
]
}
},
"groups": [
{
"name": "tenant-A",
"path": "/tenant-A",
"attributes": {},
"subGroups": [
{
"name": "tenant-A-admins",
"path": "/tenant-A/tenant-A-admins",
"attributes": {},
"roleMappings": {
"client": {
"my-app-frontend": [
"manage-users",
"manage-roles"
]
}
}
},
{
"name": "tenant-A-users",
"path": "/tenant-A/tenant-A-users",
"attributes": {},
"roleMappings": {
"client": {
"my-app-frontend": [
"view-tenants"
]
}
}
}
]
},
{
"name": "tenant-B",
"path": "/tenant-B",
"attributes": {},
"subGroups": [
{
"name": "tenant-B-admins",
"path": "/tenant-B/tenant-B-admins",
"attributes": {},
"roleMappings": {
"client": {
"my-app-frontend": [
"manage-users",
"manage-roles"
]
}
}
},
{
"name": "tenant-B-users",
"path": "/tenant-B/tenant-B-users",
"attributes": {},
"roleMappings": {
"client": {
"my-app-frontend": [
"view-tenants"
]
}
}
}
]
}
]
}
In this setup, we have realm roles like tenant-admin which is a composite role. It inherits client roles view-tenants, manage-users, and manage-roles from the my-app-frontend client. This means a user with the tenant-admin realm role automatically gets these permissions for the frontend application.
Then, we use groups to model our tenants. /tenant-A and /tenant-B are top-level groups. Within each tenant group, we have subgroups like tenant-A-admins and tenant-A-users. These subgroups are where we assign specific client roles. For example, users in tenant-A-admins get manage-users and manage-roles for my-app-frontend. Users in tenant-A-users only get view-tenants.
The power here is how groups can be nested and how role mappings can be applied at different levels. A user assigned to the tenant-A-admins group will automatically inherit those roles. If we wanted to grant a specific user elevated privileges across all tenants, we could assign them the tenant-admin realm role. If a user needs to be an admin for both tenant A and tenant B, they can simply be added to both /tenant-A/tenant-A-admins and /tenant-B/tenant-B-admins groups.
This structure allows you to manage authorization at scale by defining roles once and then assigning users to groups that have those roles. When a user logs in, Keycloak issues an Access Token containing all their assigned roles and group memberships, which your application can then use to make authorization decisions.
The most surprising thing most people don’t realize is that when you assign a role to a group, you can specify which client that role applies to. This is crucial for multi-client setups where a single role might have different meanings or permissions depending on the client consuming the token. For instance, a developer role might grant read-logs permission on a backend-api client but debug-mode permission on a monitoring-ui client.
The next logical step is to explore how to dynamically assign users to these groups based on external criteria or user attributes.