Restrict roles to a subset through the "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.

img.png

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

img_1.png

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

img_2.png

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.

img_3.png

Default and optional scopes

img_4.png

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 :

img_5.png

For our “roles” scope, 3 mappers :

img_6.png

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.

“full scope allowed” : the bomb

Too many roles and audience

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

img_7.png

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

img_8.png

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

img_9.png

And here we have the worst default parameter in Keycloak :

img_10.png

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

img_11.png

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:

img_12.png

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.