Keycloak roles restriction and full scopes : for security concerns, you must restrict roles to a subset through the « Full Scope Allowed » Switch as by default a client has « roles » scope as « default » so that a user will have all affected clients roles in its tokens. Learn with this article how and why you must restrict roles in tokens by turning off « full scope allowed » switch.

Role-based access control (RBAC)

Great (but uncomplete) post : https://medium.com/@nishada/keycloak-spring-boot-rbac-e8732a91909a

Roles for users

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.

Keycloak roles restriction and full scopes

In the user management page, we can affect roles to a user :

Keycloak roles restriction and full scopes-1

In « realm roles » entry, then a special « composite role » named « default-roles-realmname » contains all roles affected by default to each new user :

Keycloak roles restriction and full scopes

Roles in tokens

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.

Audience

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 ».

« roles » scope : the firing

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.

https://oauth.net/2/scope

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.

Keycloak roles restriction and full scopes

Default and optional scopes

Keycloak roles restriction and full scopes

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
  • email
  • profile
  • roles

Scopes mappers

Each scope is declared in « client scopes » section :

Keycloak roles restriction and full scopes

For our « roles » scope, 3 mappers :

Keycloak roles restriction and full scopes

Each mapper will add data to tokens (also in userinfo and tokenintrospect 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.

« full scope allowed » : the bomb

Too many roles and audience

Let’s write an example first with 2 frontend applications and 2 backends :

Keycloak roles restriction and full scopes

« 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 :

Keycloak roles restriction and full scopes

There is no limitation of what my token can do from the « frontend » client.

A checkbox

In your client scopes, the first one « clientname-dedicated » is a link to :

  • specific mappers for this client
  • restrict roles
Keycloak roles restriction and full scopes

And here we have the worst default parameter in Keycloak :

Keycloak roles restriction and full scopes

« 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 :

Keycloak roles restriction and full scopes

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.

Default clients in Keycloak : correctly set

account-console

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:

Keycloak roles restriction and full scopes

Conclusion

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.

Mathieu PASSENAUD
Les derniers articles par Mathieu PASSENAUD (tout voir)