Keycloak UMA 2.0: User-Managed Resource Sharing
Last updated: June 2026
UMA 2.0 — User-Managed Access — is an OAuth 2.0 extension that lets a resource owner control who can access their resources, asynchronously and without being present in each access decision. In Keycloak, UMA builds on Authorization Services and uses a permission-ticket flow: the resource server registers protected resources, the client receives a 401 response with a permission ticket, exchanges that ticket for a Requesting Party Token (RPT) at Keycloak’s token endpoint, and presents the RPT to gain access. This guide walks through the full flow with real curl examples, explains when UMA is the right architectural choice, and — equally important — explains when it is not.
What problem does UMA 2.0 solve?
Standard OAuth 2.0 RBAC handles the question “can users in role X access resource Y?” well, but it answers that question at deployment time, not at runtime. The roles are baked into the application or the authorization server configuration; individual users cannot grant or revoke access to their own resources on the fly.
Consider a document-sharing scenario. Alice uploads a file to a health-care portal. She wants to share it with her specialist, Bob, but not with anyone else — not even others with the same role. With plain RBAC, the only way to implement this is to build a custom sharing system inside the application: a join table, a set of API endpoints, and application-level enforcement. Every application that needs this pattern has to rebuild the same infrastructure.
UMA 2.0, specified by the Kantara Initiative, moves resource-level authorization out of application code and into a standards-based authorization server. Alice registers her document as a protected resource in Keycloak, sets a policy granting Bob permission, and the authorization server handles enforcement. The application only needs to validate the RPT it receives.
For a broader grounding in Keycloak’s authorization architecture, read Fine-Grained Authorization in Keycloak Explained and the policy types available in Keycloak Authorization Services: Policy Types Explained.
The actors in UMA 2.0
Before tracing the flow, it is worth naming the four parties that UMA defines:
- Resource Owner — the user who owns the resource (Alice in the example above). She can set permissions through Keycloak’s account console without touching the application.
- Resource Server (RS) — the API or application that hosts the resource. It registers resources with Keycloak and enforces access by validating RPTs.
- Client — the application acting on behalf of the Requesting Party (Bob’s browser, a mobile app, a service).
- Requesting Party (RqP) — the end user or service that wants access (Bob). In some scenarios the Requesting Party and the Client are the same party, but UMA keeps them conceptually distinct.
- Authorization Server (AS) — Keycloak, which issues tickets, evaluates policies, and mints RPTs.
Enabling UMA on a Keycloak client
UMA in Keycloak is scoped to a specific client that represents your resource server. The client must have Authorization Services enabled, and within that, User-Managed Access must be turned on.
In the Keycloak Admin Console (v26.x):
- Open your realm and navigate to Clients.
- Select the client that represents your resource server (or create one with
Access Type: confidential). - Go to the Authorization tab.
- Enable the User-Managed Access toggle.
- Save.
Keycloak will now create a dedicated uma_protection scope for this client and expose a Protection API endpoint the resource server can call to register resources.
You can also configure this via the Admin REST API:
curl -s -X PUT
"https://keycloak.example.com/admin/realms/myrealm/clients/{client-uuid}"
-H "Authorization: Bearer ${ADMIN_TOKEN}"
-H "Content-Type: application/json"
-d '{
"authorizationServicesEnabled": true,
"attributes": {
"user.managed.access.enabled": "true"
}
}'
Registering a protected resource
The resource server registers each resource it wants to protect by calling Keycloak’s Protection API. This requires a Protection API Token (PAT), which is an access token obtained by the resource server’s service account with the uma_protection scope.
Step 1 — Obtain a PAT:
PAT=$(curl -s -X POST
"https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token"
-H "Content-Type: application/x-www-form-urlencoded"
-d "grant_type=client_credentials"
-d "client_id=my-resource-server"
-d "client_secret=${CLIENT_SECRET}"
| jq -r '.access_token')
Step 2 — Register the resource:
curl -s -X POST
"https://keycloak.example.com/realms/myrealm/authz/protection/resource_set"
-H "Authorization: Bearer ${PAT}"
-H "Content-Type: application/json"
-d '{
"name": "Alice Document 42",
"type": "urn:myapp:resources:document",
"resource_scopes": ["read", "write"],
"owner": "alice",
"ownerManagedAccess": true,
"uris": ["/documents/42"]
}'
Setting ownerManagedAccess: true tells Keycloak that Alice (the owner) can manage permissions through the account console. The response includes a _id field — save it; the resource server uses it when requesting permission tickets.
The permission ticket flow, step by step
This is the heart of UMA. The sequence below follows Bob (Requesting Party) trying to read Alice’s document via a client application.
Step 1 — Client hits the resource, resource server returns 401 + ticket
Bob’s client sends a request to the resource server without (or with an insufficient) token:
GET /documents/42 HTTP/1.1
Host: api.example.com
The resource server recognizes that the request lacks the required authorization. It calls Keycloak’s permission endpoint to obtain a permission ticket:
TICKET=$(curl -s -X POST
"https://keycloak.example.com/realms/myrealm/authz/protection/permission"
-H "Authorization: Bearer ${PAT}"
-H "Content-Type: application/json"
-d '[{
"resource_id": "RESOURCE_UUID",
"resource_scopes": ["read"]
}]'
| jq -r '.ticket')
The resource server responds to the client with:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: UMA realm="myrealm",
as_uri="https://keycloak.example.com/realms/myrealm",
ticket="eyJhbGciOiJIUzI1NiIsInR5..."
Step 2 — Client exchanges the ticket for an RPT
The client takes the ticket and the Requesting Party’s identity token and calls Keycloak’s token endpoint using the urn:ietf:params:oauth:grant-type:uma-ticket grant type:
RPT=$(curl -s -X POST
"https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token"
-H "Content-Type: application/x-www-form-urlencoded"
-d "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket"
-d "ticket=${TICKET}"
-d "client_id=my-client-app"
-d "client_secret=${CLIENT_APP_SECRET}"
-H "Authorization: Bearer ${BOB_ACCESS_TOKEN}"
| jq -r '.access_token')
At this point Keycloak evaluates the policies Alice set for her document. If Bob is in Alice’s permission list with the read scope, Keycloak issues an RPT — a standard JWT with a authorization.permissions claim listing the granted permissions.
If Keycloak denies the request (Bob is not in Alice’s policy), it returns:
{
"error": "access_denied",
"error_description": "not_authorized"
}
Some UMA implementations also return 403 Forbidden with a fresh ticket so the client can attempt interactive authorization (for example, prompting Alice to approve Bob’s access in real time). Keycloak supports this via the pending authorization request mechanism in the account console.
Step 3 — Client presents the RPT
GET /documents/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5...
The resource server validates the RPT (either by calling Keycloak’s token introspection endpoint or by verifying the JWT signature locally and inspecting the authorization.permissions claim) and serves the document if the read scope is present.
Introspection approach:
curl -s -X POST
"https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token/introspect"
-H "Content-Type: application/x-www-form-urlencoded"
-d "token=${RPT}"
-d "client_id=my-resource-server"
-d "client_secret=${CLIENT_SECRET}"
| jq '.authorization.permissions'
For more on token validation patterns see Keycloak Token Validation for APIs and Keycloak Token Exchange: A Practical Guide.
Resource owner: managing permissions via the account console
One of UMA’s distinguishing features is that resource owners can manage their own permissions without logging into an admin dashboard. Keycloak’s account console exposes a My Resources section (at /realms/{realm}/account/#/resources) when UMA is enabled.
From this UI, Alice can:
- View all resources she owns.
- Grant a specific user (Bob) access to a specific resource with a specific scope.
- Revoke permissions she has previously granted.
- Approve or deny pending access requests from other users.
This is the user-managed part of User-Managed Access. The authorization server enforces it; the application never has to touch a permissions table.
Setting policies programmatically (for resource owners)
In scenarios where you want to automate policy creation on behalf of a resource owner — for example, when a user uploads a file and your app automatically creates a share link — the resource owner can call the Policy API:
curl -s -X POST
"https://keycloak.example.com/realms/myrealm/authz/protection/uma-policy/RESOURCE_UUID"
-H "Authorization: Bearer ${ALICE_ACCESS_TOKEN}"
-H "Content-Type: application/json"
-d '{
"name": "Share with Bob",
"description": "Grants Bob read access to document 42",
"scopes": ["read"],
"users": ["bob"]
}'
Note that this endpoint uses Alice’s access token, not the PAT. Only the resource owner (or a realm admin) can set resource-level user policies this way.
Validating RPT permissions in code
Most Keycloak client libraries can handle RPT validation. Here is a minimal Node.js example using raw JWT decoding:
import jwt from 'jsonwebtoken';
function hasPermission(rpt, resourceId, scope) {
// Verify signature using Keycloak's public key (fetch from JWKS endpoint)
const decoded = jwt.decode(rpt); // Use jwt.verify() with the public key in production
const permissions = decoded?.authorization?.permissions ?? [];
return permissions.some(
(p) => p.rsid === resourceId && p.scopes?.includes(scope)
);
}
// Usage in Express middleware
app.get('/documents/:id', (req, res) => {
const rpt = req.headers.authorization?.replace('Bearer ', '');
if (!rpt || !hasPermission(rpt, req.params.id, 'read')) {
return res.status(403).json({ error: 'Forbidden' });
}
// serve resource
});
For a deeper look at OAuth 2.0 token flows see OAuth 2.0: A Developer’s Visual Guide.
When NOT to use UMA
UMA adds significant complexity. Before committing to it, ask yourself whether any of these apply:
Your access model is role-based, not resource-instance-based. If all users with role editor can edit all documents, RBAC with Keycloak’s standard Authorization Services (resource server policies + role policies) is simpler and better understood. UMA shines when permissions differ per resource instance, not per role.
You don’t have user-controlled sharing. If resource owners never need to grant or revoke access themselves — only admins do — the standard Authorization Services policy API is sufficient. You get fine-grained authorization without the ticket flow overhead.
Your team is new to Keycloak. The UMA flow introduces three token types (access token, PAT, RPT), a permission ticket, a Protection API, and a Policy API. Teams still getting comfortable with standard OIDC and Authorization Services will find UMA a steep learning curve. Start with Fine-Grained Authorization in Keycloak Explained first.
Latency is critical. The ticket flow requires at minimum two additional round-trips to Keycloak per request (obtain ticket, exchange for RPT) before the actual resource request succeeds. RPT caching on the client helps, but the initial access always incurs this overhead.
You are building a simple internal tool. UMA is designed for federated, multi-stakeholder access control. If your stakeholders are a single engineering team, the complexity cost is rarely justified.
The majority of applications that need fine-grained authorization are better served by Keycloak Authorization Services without UMA — resource-based permissions, scope-based policies, and role or group policies cover most production use cases cleanly.
UMA 2.0 and the broader OAuth 2.0 landscape
UMA 2.0 extends the OAuth 2.0 authorization framework. The permission ticket is analogous to an authorization code in the standard authorization code flow: a short-lived, single-use credential that the client exchanges for a more capable token. The RPT itself is a standard Bearer token — resource servers that already know how to validate JWTs or call introspection endpoints need only add the authorization.permissions claim check.
UMA also fits naturally alongside token exchange scenarios. A client that already holds a valid access token for one resource server can use token exchange to obtain an RPT for a different resource server, all within the same Keycloak realm. See Keycloak Token Exchange: A Practical Guide for how that pattern works.
If you are designing an API platform where multiple resource servers participate in a single authorization domain — for example, a healthcare data platform where patients (resource owners) control which providers (requesting parties) can access their records — UMA 2.0 is the right standard. It gives you the protocol plumbing so you do not have to invent it.
Frequently asked questions
What is UMA 2.0 in Keycloak?
UMA 2.0 (User-Managed Access 2.0) is an OAuth 2.0 extension profile that enables resource owners to authorize access to their resources for other parties without being present at the moment of access. In Keycloak, it is implemented as part of Authorization Services. When UMA is enabled on a client, resource owners can manage permissions through Keycloak’s account console, and clients access protected resources using a permission-ticket flow that results in a Requesting Party Token (RPT).
What is a permission ticket?
A permission ticket is a short-lived, opaque token that a resource server obtains from Keycloak’s Protection API when a client tries to access a resource it is not yet authorized for. The resource server returns the ticket to the client in a 401 Unauthorized response (via the WWW-Authenticate header). The client then exchanges the ticket — along with its own credentials and the requesting party’s identity token — for an RPT at Keycloak’s token endpoint using the urn:ietf:params:oauth:grant-type:uma-ticket grant type. The ticket is single-use and expires quickly (default: 300 seconds in Keycloak).
What is an RPT?
A Requesting Party Token (RPT) is a signed JWT issued by Keycloak at the end of a successful UMA ticket exchange. It contains a standard set of OAuth 2.0 claims plus an authorization.permissions claim that lists every resource and scope the requesting party has been granted. The resource server validates the RPT (by signature verification or introspection) and uses the permissions claim to make the final access decision. RPTs are Bearer tokens — any existing JWT validation infrastructure can process them with minimal changes.
Do I need UMA or is RBAC enough?
For most applications, RBAC with Keycloak’s standard Authorization Services is enough. Use UMA when: (1) individual resource owners — not just administrators — need to grant or revoke access to specific resource instances at runtime, and (2) you want that sharing to be enforced by the authorization server rather than by application-level code. If your access model is “all editors can edit all documents,” RBAC is simpler. If your model is “Alice decides which specific documents Bob can read,” UMA adds clear value.
How does UMA differ from standard Keycloak Authorization Services?
Keycloak Authorization Services (without UMA) lets administrators and application owners define policies in the Admin Console: resource-based policies, role policies, group policies, rule-based policies. Clients then request permissions directly, and the authorization server evaluates them. UMA adds two things on top: (1) the permission-ticket flow, which lets a resource server trigger authorization asynchronously rather than having the client request permissions upfront; and (2) owner-managed policies, which let end users (not just admins) create and revoke policies for their own resources through the account console. For a full breakdown of policy types see Keycloak Authorization Services: Policy Types Explained.
Conclusion
UMA 2.0 solves a real problem: letting users share their own resources with other users without baking a custom permissions system into every application. In Keycloak 26.x it is a production-ready feature that sits on top of a solid Authorization Services foundation. The permission-ticket flow adds two round-trips but keeps the resource server decoupled from policy decisions. The account console gives resource owners a no-code way to manage sharing.
The key takeaways:
- Enable UMA per client via the User-Managed Access toggle in the Authorization tab.
- Resource servers register resources and obtain permission tickets via the Protection API using a PAT.
- Clients exchange tickets for RPTs at
/realms/{realm}/protocol/openid-connect/tokenwithgrant_type=urn:ietf:params:oauth:grant-type:uma-ticket. - Resource owners manage permissions through
/realms/{realm}/account/#/resourcesor the Policy API. - Most applications do not need UMA — standard Authorization Services or simple RBAC is the right default.
If you are implementing UMA on a self-managed Keycloak cluster, the infrastructure overhead of keeping Authorization Services performant at scale is non-trivial. Skycloak handles the Keycloak infrastructure — clustering, upgrades, monitoring, and the Authorization Services configuration — so your team can focus on the access-control logic that makes your product unique.
Ready to simplify your authentication?
Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.