Restrict roles to a subset through the "full scope allowed" switch
Great (but uncomplete) post : https://medium.com/@nishada/keycloak-spring-boot-rbac-e8732a91909a
Roles are special attributes declared in clients (or direct in the realm) then affected to users. A user has a set of roles affected, just like any kind of attribute.
In the user management page, we can affect roles to a user :
In “realm roles” entry, then a special “composite role” named “default-roles-realmname” contains all roles affected by default to each new user :
From your authentication client, in the “client scopes” tab then “evaluate” subtab you can simulate an authentication for a user and get the token content.
Roles are added in the access token like this :
{
...
"realm_access": {
"roles": [
"default-roles-master",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"order-backend": {
"roles": [
"customer"
]
},
...
}
Through the “roles” scope. Disable the “roles” scope in the client and all roles will disappear.
In “aud” section in the token :
"aud": [
"order-backend",
"account"
],
It defines the clients at which the token can be used. In this example, “account” backend (a Keycloak application provided by default) and my custom “order-backend”.
https://datatracker.ietf.org/doc/html/rfc6749#section-3.3
the authorization server uses the “scope” response parameter to inform the client of the scope of the access token issued.
Scope is a mechanism in OAuth 2.0 to limit an application’s access to a user’s account. An application can request one or more scopes, this information is then presented to the user in the consent screen, and the access token issued to the application will be limited to the scopes granted.
Then, during the authentication process you have the “consent screen” displayed. You give consent for a set of scopes, called by the application.
A “default” scope is always applied, an “optional” should be called by the application during the login process.
Without any scope called, in this client (with only default parameters), we have :
- acr
- basic
- profile
- roles
Each scope is declared in “client scopes” section :
For our “roles” scope, 3 mappers :
Each mapper will add data to tokens (also in user_info and token_introspect responses).
- “client roles”: get all clients roles (affected to the user) then adds them to tokens
- “realm roles”: the same for realm roles
- “audience resolve”: populates the “aud” field with all clients the user has roles affected.
Let’s write an example first with 2 frontend applications and 2 backends :
“frontend” will never ask “products-management-backend”, even if the user has roles from “products-management-backend” client.
Let give a try, my token is :
{
"exp": 1733135718,
"iat": 1733135658,
"jti": "54f4b469-6d02-44d1-8906-da18db54c367",
"iss": "https://zugjjqptlllqkwkawayw-keycloak.services.clever-cloud.com/realms/master",
"aud": [
"order-backend",
"master-realm",
"account",
"search-backend",
"products-management-backend"
],
"sub": "97675ccc-adbd-40df-8031-ee6f31546658",
"typ": "Bearer",
"azp": "frontend",
"sid": "2828e11a-aa0f-4e23-b782-e94a61a6c046",
"acr": "1",
"allowed-origins": [
"/*"
],
"realm_access": {
"roles": [
"default-roles-master",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"order-backend": {
"roles": [
"manager",
"support",
"customer"
]
},
"master-realm": {
"roles": [
"query-users"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
},
"search-backend": {
"roles": [
"search"
]
},
"products-management-backend": {
"roles": [
"add"
]
}
},
"scope": "openid profile email",
"email_verified": false,
"name": "Mathieu Passenaud",
"preferred_username": "standard-user",
"given_name": "Mathieu",
"family_name": "Passenaud",
"email": "contact@please-open.it"
}
😳
“aud” contains all clients, and more with 2 defaults clients :
- account
- realm-management : yes, my user also has a realm management role (query-users) for administrative tasks.
A quick draw of the reality :
There is no limitation of what my token can do from the “frontend” client.
In your client scopes, the first one “clientname-dedicated” is a link to :
- specific mappers for this client
- restrict roles
And here we have the worst default parameter in Keycloak :
“Allows you to disable all restrictions.”
WHY ALL RESTRICTIONS ARE DISABLED BY DEFAULT ?
Uncheck this nightmare checkbox and assign only roles needed for this client :
Now my generated token :
{
"exp": 1733139265,
"iat": 1733139205,
"jti": "fc9ca70d-094e-4165-9c10-89ca50fa5ede",
"iss": "https://zugjjqptlllqkwkawayw-keycloak.services.clever-cloud.com/realms/master",
"aud": [
"order-backend",
"search-backend"
],
"sub": "97675ccc-adbd-40df-8031-ee6f31546658",
"typ": "Bearer",
"azp": "frontend",
"sid": "eefde5bf-f1ee-433d-9f41-13dbcf0eaf67",
"acr": "1",
"allowed-origins": [
"/*"
],
"resource_access": {
"order-backend": {
"roles": [
"manager",
"support",
"customer"
]
},
"search-backend": {
"roles": [
"search"
]
}
},
"scope": "openid profile email",
"email_verified": false,
"name": "Mathieu Passenaud",
"preferred_username": "standard-user",
"given_name": "Mathieu",
"family_name": "Passenaud",
"email": "contact@please-open.it"
}
“aud” and “resource_access” are now correctly restricted with only needed attributes, no more.
The “account-console” client corresponds to the management account console provided by Keycloak. The “account api” is behind the client “account”.
By default, “full scope allowed” is disabled with correct restrictions:
Never trust default parameters, you should keep a hand on every parameter in Keycloak.
Get into “client”, “client scopes” then “evaluate” and see exactly what is in tokens.