Fine-Grained Authorization in Keycloak Explained
Last updated: March 2026
What is fine-grained authorization in Keycloak?
Fine-grained authorization in Keycloak is an access control model that evaluates decisions based on users, resources, scopes, attributes, and runtime context — going well beyond static role-based access control (RBAC). It is built on the UMA 2.0 (User-Managed Access) standard, which lets resource owners securely control access to their assets even when those assets are hosted on different systems. Using UMA principles, Keycloak evaluates “who can do what on which resource” dynamically at request time — including conditions such as time of day, user attributes, or group membership.
A Quick Overview of Permission Evaluation
Keycloak returns permissions through an artifact called the Requesting Party Token (RPT) — a JWT token that contains the evaluated access rights. You can inspect RPTs using the JWT Token Analyzer. There are three main ways to obtain authorization decisions:
- Explicit Permission Ticket Flow
- Direct Token Request Flow (RPT returned)
- Direct Token Request Flow returning decisions/permissions in JSON (no RPT)
Options 1 and 2 return an RPT token that the resource server needs to validate and interpret.
Option 3 bypasses JWT parsing and instead returns a plain JSON response representing decisions or permissions directly.
In this article, we primarily focus on Option 3, though we briefly outline the other two for context.
Option 1 — Explicit Permission Ticket Flow
This is the full UMA negotiation process and is needed only when permission requests must be dynamically raised per resource or per request.
Steps:
- Resource server calls
/authz/protection/permission-> gets permission ticket (Requires PAT — Protection API Token) - Client then exchanges ticket at
/protocol/openid-connect/tokenusinggrant_type=urn:ietf:params:oauth:grant-type:uma-ticket
Option 2 — Directly Access Token Endpoint
Here we directly invoke with user access token as Bearer token:
POST /protocol/openid-connect/token
grant_type=urn:ietf:params:oauth:grant-type:uma-ticket
audience=<resource-server-client-id>
Authorization: Bearer <user-access-token>
This approach works best when:
- All resources and scopes already exist
- No dynamic resource creation or ad-hoc permission requests are required
- A single evaluation pass is enough
- User identity maps naturally from the bearer token
Keycloak returns an RPT token, which the application must decode and interpret.
Note: In the above, we could optionally send a permission parameter in the body.
Option 3 — Direct Token Request Returning Decisions or Permissions (JSON)
This is a simplified approach where the client does not receive an RPT token.
Instead, Keycloak responds with evaluation results directly:
granted = trueor response to indicate failure, or- a list of identified permissions
For more details on authorization, please refer to the Keycloak Authorization Services Guide.
Practical Example Use Case
According to Keycloak documentation:
“A permission associates the object being protected with the policies evaluated to determine whether access is granted.”
Permissions typically express a rule such as:
User X can perform action Y on resource Z
Where:
- X -> user, group, role, or contextual attribute
- Y -> read, update, approve, delete
- Z -> product, order, account, document, etc.
Let us define a business requirement:
A Supervisor (Bob) can update any order, but only within an allowed timeframe.
For a step-by-step guide on implementing attribute-based policies that complement this approach, see our tutorial on Keycloak ABAC configuration.
Pre-requisite:
A confidential client with Authorization Services Enabled
Step 1 — Create Resource
Navigate to:
Client -> Authorization -> Resources -> Create
Fill values:
| Field | Value |
|---|---|
| Name | order_1 |
| Type | order |
| Display Name | Order |
| URI | /orders/order_1 |
Note: If per-order rules exist, create order_1, order_2, etc.
Add scope update for this resource.
Step 2 — Create Policies
2.1 Supervisor Policy
Path:
Authorization -> Policies -> Create -> Role-based
Values:
| Field | Value |
|---|---|
| Name | supervisor-can-modify |
| Realm Role | supervisor |
This policy grants Bob supervisory privileges. He has the realm role supervisor initially assigned.
2.2 Time Policy
Path:
Authorization -> Policies -> Time Policy
Configure allowed time window.
Assume we call it:
allowed-time-policy
Note: By default, time policy evaluates realm timezone.
Step 3 — Create Permission
Path:
Authorization -> Permissions -> Scope-Based Permission
| Field | Value |
|---|---|
| Name | update-order_1 |
| Apply to Resource Type | order |
| Scope | update |
| Policies | supervisor-can-modify, allowed-time-policy |
| Decision Strategy | Unanimous |
Decision Strategy: Unanimous -> both policies must evaluate to TRUE.
Since we specified Apply to Resource Type as order, it applies permission to all resources of that type — not just to order_1.
Expected Runtime Evaluation
Keycloak effectively executes:
IF (user has role SUPERVISOR)
AND (current time is within allowed window)
THEN
access = granted
ELSE
access = denied
This becomes part of the JSON decision.
Testing the Flow
- Obtain user access token
- Make request to the token endpoint similar to the following, and the result will yield true if permission is granted:
curl -X POST "{{baseURL}}/realms/acmeauth/protocol/openid-connect/token"
-H "Authorization: Bearer <YOUR_BEARER_USER_TOKEN>"
-H "Content-Type: application/x-www-form-urlencoded"
-d "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket"
-d "permission=order_1#update"
-d "audience=orderauthservice"
-d "response_mode=decision"
Where baseURL points to your Skycloak hostname.
Summary
Keycloak authorization services enable precise access decisions — beyond static RBAC — using UMA-based policies. Among the three approaches described:
For REST-based microservices, Option 3 often yields the lowest overhead and cleanest design. Option 1 is a requirement for special use cases (more dynamic permission at runtime).
To learn more about how Skycloak supports role-based and fine-grained authorization, visit our RBAC feature page. For details on securing your Keycloak environment, see our security overview.
Skycloak provides production-ready managed Keycloak hosting, helping teams avoid the complexity of maintaining and scaling Keycloak themselves.
If you’re new to Skycloak, visit the Skycloak Getting Started Guide to learn more. Explore the full documentation or review pricing plans to find the right fit.
Frequently asked questions
What is the difference between RBAC and fine-grained authorization in Keycloak?
RBAC grants access based on a user’s roles alone — if you have the admin role, you can do everything an admin can do. Fine-grained authorization layers additional conditions on top of roles: the same admin user might only be allowed to update orders during business hours, or only orders belonging to their department. Keycloak’s authorization services combine role policies, time policies, attribute policies, and more into a single permission evaluation.
What is an RPT token in Keycloak?
An RPT (Requesting Party Token) is a JWT issued by Keycloak’s authorization server that contains the evaluated permissions for a specific resource request. It is the artifact returned by the UMA grant flow (Options 1 and 2 described above). Your resource server validates the RPT to determine what actions the bearer is allowed to perform. You can inspect an RPT with the JWT Token Analyzer.
When should I use response_mode=decision vs an RPT?
Use response_mode=decision (Option 3) when you want a simple granted: true/false JSON response and do not need your resource server to parse a full JWT. Use the RPT (Options 1 or 2) when the resource server needs to inspect the granted permissions itself — for example, to determine which scopes were approved — or when the full UMA negotiation is required for dynamic, per-resource permission tickets.
Does fine-grained authorization work with Keycloak 26?
Yes. Keycloak’s authorization services (UMA, policy evaluation, RPTs) are fully supported in the current Quarkus-based distribution including Keycloak 26.x. The Admin Console paths referenced in this article (Client → Authorization → Resources / Policies / Permissions) are unchanged. Confirm endpoint paths using your realm’s discovery document at /realms/{realm}/.well-known/openid-configuration.
Ready to simplify your authentication?
Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.