Keycloak Client Scopes vs Roles: When to Use Each

Guilliano Molaire Guilliano Molaire 9 min read

Last updated: June 2026

Roles and client scopes solve different problems in Keycloak. Roles represent what a user is allowed to do — authorization, RBAC — and land in the token’s realm_access and resource_access claims. Client scopes are reusable bundles of protocol mappers and role scope that control which claims and scopes end up in a token for a given client. Use roles to model permissions; use client scopes to shape token contents and handle OAuth scope requests. Most production setups use both together.

If you have spent time in the Keycloak admin console, you have probably felt the pull of a conceptual overlap: both roles and client scopes feel like ways to “grant access.” The confusion is understandable, but the two mechanisms sit at different layers of Keycloak’s authorization and token-issuance pipeline. Getting them straight pays dividends — cleaner tokens, easier auditing, and a more maintainable authorization model as your system grows.

Roles in Keycloak: Modeling Permissions

Keycloak’s role-based access control documentation defines roles as named permission units that you assign to users or groups. There are three varieties.

Realm roles vs client roles vs composite roles

Realm roles are defined at the realm level and apply across all clients in that realm. They show up in the token under realm_access.roles. A role like admin or billing-viewer defined at the realm level is a good fit when the permission it represents spans multiple services.

Client roles are scoped to a specific client (application). They appear under resource_access.<client-id>.roles. A role like api-gateway:read or dashboard:editor makes sense as a client role because it only has meaning within a single application context. Client roles keep your realm-level namespace clean and make it easier to reason about what each application controls.

Composite roles are roles that include other roles. A composite superadmin realm role might include billing-viewer, user-manager, and report-exporter. Composites reduce repetitive role assignments on users but can make auditing harder if overused — a user assigned to superadmin implicitly holds all the contained roles, which may not be obvious at a glance.

How roles appear in the access token

A decoded JWT access token from Keycloak for a user holding the realm role editor and the client role dashboard:viewer looks like this:

{
  "realm_access": {
    "roles": ["editor", "offline_access", "uma_authorization"]
  },
  "resource_access": {
    "dashboard": {
      "roles": ["viewer"]
    },
    "account": {
      "roles": ["manage-account", "view-profile"]
    }
  }
}

Your API or backend reads these claims and decides whether to authorize the request. For a deeper look at how tokens flow through their lifecycle, see the post on JWT token lifecycle management, expiration, refresh, and revocation strategies.

Roles by themselves do not determine whether a claim appears in a token. That is the job of client scopes.

Client Scopes: Shaping Token Contents

A client scope is a reusable configuration object that groups together:

  • Protocol mappers — rules that map user attributes, realm roles, or custom data into token claims
  • Scope restrictions — which realm roles or client roles are allowed to flow into tokens issued for clients that include this scope

Think of a client scope as a named “token profile.” You define it once and attach it to many clients. The Keycloak client scopes documentation covers the full configuration surface.

Default vs optional client scopes

Every client in Keycloak has two lists of client scopes:

Default client scopes are always included in tokens issued for that client, regardless of the OAuth scope parameter in the authorization request. The built-in profile, email, roles, and web-origins scopes are default on most clients.

Optional client scopes are only included when the client explicitly requests them via the scope parameter in the authorization request. For example, a client requesting scope=openid email api.read will get the claims from the email and api.read scopes included, provided those are configured as optional scopes on that client.

This separation lets you keep base tokens lean while allowing clients to request richer claim sets only when needed.

Protocol mappers inside client scopes

Protocol mappers are the actual engines that produce claims. Inside any client scope, you can add mappers of types such as:

  • User attribute mapper — maps a custom user attribute (e.g., department) to a token claim
  • Realm role mapper — maps realm roles into a claim (by default, the roles scope contains a mapper that populates realm_access.roles)
  • Hardcoded claim mapper — injects a static value into every token for clients using this scope
  • Audience mapper — adds the aud claim so downstream services can verify the token was meant for them

For practical examples of using mappers to embed custom data, see using custom user attributes in Keycloak OIDC tokens.

The scope parameter and optional scopes

When a client sends an authorization request, the scope query parameter lists the OAuth scopes it wants. Keycloak maps those scope strings to optional client scopes by name. If the string matches an optional scope configured on the client, that scope’s mappers and role restrictions are activated for that token.

This is the standard OAuth 2.0 scope mechanism — it is how a single-page app can request only openid email for a login flow but also request api.read when it needs to call a protected API.

Full Scope Allowed and Token Bloat

One of the most consequential settings on a Keycloak client is Full Scope Allowed (Admin Console path: Clients → [your client] → Client Scopes → Full scope allowed). When this toggle is on — which it is by default — Keycloak includes all realm roles and all client roles the user holds in every token issued for that client.

For internal tooling with a small user base, this is convenient. For production APIs, it creates two problems:

  1. Token bloat: A user with a dozen roles gets a large JWT. Large JWTs increase request sizes, hit header-size limits on some proxies, and slow down validation.
  2. Over-privileged tokens: A token issued for a low-trust client (a marketing website, for example) should not carry roles that only matter for your billing API. Exposing all roles in every token increases the blast radius of a token leak.

The fix is to disable Full Scope Allowed and instead use the Scope tab on the client to explicitly list which realm roles may appear in tokens for that client. This is called “partial scope.” Combined with client-role granularity, partial scope is a meaningful security control — a principle aligned with what Keycloak’s fine-grained authorization model is built around.

Admin console path for partial scope: Clients → [client] → Client scopes → Assigned client scopes → toggle off “Full Scope Allowed” on the Scope tab.

Audience Mappers

A frequent source of token validation failures is a missing or incorrect aud (audience) claim. By default, the access token’s aud is set to the client that requested it. If your backend API validates the aud claim, tokens requested by a frontend SPA will fail validation when presented to the API.

The solution is an Audience mapper inside a client scope:

  1. Create or open a client scope (e.g., api.read)
  2. Add a mapper of type Audience
  3. Set Included Client Audience to the client ID of your API
  4. Assign the scope as optional on your frontend client

Now when the SPA requests scope=openid api.read, the resulting token carries "aud": ["your-api-client-id"], and your API’s token validator accepts it. This pattern connects directly to how Keycloak handles authorization services and policy types.

Concrete Example: api.read Scope vs editor Role

To make the distinction tangible, here is how you would model read access to an API using both mechanisms.

The editor realm role (Admin Console path: Realm settings → Roles → Add role):

  • Name: editor
  • Assigned to: users who should be able to edit content
  • Appears in token: realm_access.roles: ["editor"]
  • Your API reads this claim and permits write operations

The api.read optional client scope (Admin Console path: Client scopes → Create client scope):

  • Name: api.read
  • Protocol: OpenID Connect
  • Add a mapper: Audience → include my-api as audience
  • Add a mapper: Hardcoded claimtoken_use: access (optional metadata)
  • Optionally: add a Realm role mapper restricted to the reader role only
  • Assigned to the SPA client as optional

When the SPA requests scope=openid api.read, the resulting token gains the audience claim and any mappers configured in that scope. The user still needs the editor or reader realm role to get the corresponding realm_access.roles entries — the scope alone does not grant roles.

In short: roles decide the authorization result; client scopes decide the token shape.

Roles vs Client Scopes: Quick Reference

Dimension Roles Client Scopes
Primary purpose Model permissions and user capabilities Shape token contents (claims, mappers, scope)
Where it appears in token realm_access.roles / resource_access.<id>.roles Controls which mappers run; adds claims per scope
Granted how Assigned to users, groups, or service accounts Attached to clients as default or optional scopes; activated via OAuth scope parameter
Scope Realm-wide (realm roles) or per-client (client roles) Realm-wide; reusable across multiple clients
Typical use RBAC: admin, billing-viewer, dashboard:editor Token profiling: email, profile, api.read, audience injection
Keycloak admin path Realm settings → Roles; Clients → [client] → Roles Client scopes (top-level menu); Clients → [client] → Client scopes
Version Keycloak 26.x Keycloak 26.x

For a full exploration of role-based access patterns, see the post on understanding RBAC and protecting your digital access.

Combining Roles and Client Scopes in Practice

Most non-trivial Keycloak deployments use roles and client scopes together:

  1. Define roles that map to real business permissions (report:viewer, billing:admin, api:write).
  2. Assign roles to users via groups — avoid direct user-to-role assignments at scale.
  3. Create client scopes for each logical API surface your system exposes (reports.read, billing.admin, api.write).
  4. Add audience mappers inside each scope so the relevant backend service accepts the token.
  5. Disable Full Scope Allowed on all public-facing clients and add partial scope restrictions.
  6. Use optional scopes for elevated capabilities that not every client needs by default.

This keeps tokens lean, makes authorization auditable, and gives you a clean separation between the identity layer (who the user is and what roles they hold) and the protocol layer (what information flows into tokens for specific clients). For fine-grained policy enforcement beyond simple role checks, see Keycloak authorization services and policy types.

Skycloak’s managed Keycloak platform gives you these controls without the operational overhead of running and upgrading Keycloak yourself. The full client scope and role management UI is available in every Skycloak environment.

Frequently Asked Questions

What is the difference between a role and a client scope in Keycloak?

A role is a named permission unit assigned to users or groups — it answers “what is this user allowed to do?” Client scopes are configuration bundles attached to clients that control which claims and scopes appear in tokens — they answer “what information gets included in this token for this client?” Roles show up in token claims (realm_access.roles, resource_access). Client scopes activate protocol mappers that shape the token’s claim set. You need both: roles for authorization logic, client scopes for token hygiene.

What is Full Scope Allowed in Keycloak?

Full Scope Allowed is a per-client toggle (Clients → [client] → Client scopes tab) that, when enabled, causes Keycloak to include all realm roles and client roles the user holds in every token issued for that client. It is enabled by default. Disabling it and using the Scope tab to list only the roles relevant to that client is a security best practice — it prevents tokens from carrying unnecessary role claims and reduces token size. For production environments, Full Scope Allowed should be off on all public-facing clients.

How do Keycloak roles appear in the access token?

Realm roles appear under the realm_access.roles array in the JWT access token. Client roles appear under resource_access.<client-id>.roles. Both are present by default because the built-in roles client scope (which is a default scope on most clients) includes a role mapper that populates these claims. If roles are missing from a token, check that the roles scope is attached as a default scope on the client, that Full Scope Allowed is on or the specific role is in the allowed scope, and that the user is actually assigned the role.

Can I use client scopes without roles?

Yes. Client scopes are independent of roles. You can use a client scope purely to inject protocol mapper outputs — for example, a department scope that maps the user’s department custom attribute into a token claim. No role needs to be involved. Scopes and roles are complementary, not dependent on each other.

How does the OAuth scope parameter interact with Keycloak’s optional client scopes?

When a client sends an authorization request with a scope parameter (e.g., scope=openid email api.read), Keycloak looks up each scope string against the optional client scopes configured on that client. Matching optional scopes are activated for that token issuance — their mappers run and their role scope restrictions apply. Default client scopes are always activated regardless of the scope parameter. This is how you implement least-privilege token issuance: keep the default token minimal, and let clients opt into richer scopes only when needed.


Managing Keycloak roles and client scopes at scale is straightforward when the mental model is clear: roles express authorization, client scopes express token shape. Combining partial scope, audience mappers, and well-named optional scopes gives you a system that is secure, auditable, and easy to extend as new services are added.

Ready to run Keycloak without managing the infrastructure yourself? See Skycloak’s managed Keycloak plans.

Guilliano Molaire
Written by Guilliano Molaire Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Ready to simplify your authentication?

Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.

© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman