keycloak/keycloak · error · IllegalArgumentException
You must either provide a permission ticket or the permissio
Error message
You must either provide a permission ticket or the permissions you want to request.
What it means
Thrown by HttpMethodAuthenticator.uma(AuthorizationRequest) as an IllegalArgumentException when both request.getTicket() and request.getPermissions() are null. UMA authorization requires at least one of: a permission ticket obtained from the Protection API, or a set of permissions the client is requesting directly. This is a caller-side precondition failure caught before any HTTP call is made.
Source
Thrown at authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java:87
public HttpMethod<R> uma() {
// if there is an authorization bearer header authenticate using bearer token
Header authorizationHeader = method.builder.getFirstHeader("Authorization");
if (!(authorizationHeader != null && authorizationHeader.getValue().toLowerCase().startsWith("bearer"))) {
client();
}
method.params.put(OAuth2Constants.GRANT_TYPE, Arrays.asList(OAuth2Constants.UMA_GRANT_TYPE));
return method;
}
public HttpMethod<R> uma(AuthorizationRequest request) {
String ticket = request.getTicket();
PermissionTicketToken permissions = request.getPermissions();
if (ticket == null && permissions == null) {
throw new IllegalArgumentException("You must either provide a permission ticket or the permissions you want to request.");
}
uma();
method.param("ticket", ticket);
method.param("claim_token", request.getClaimToken());
method.param("claim_token_format", request.getClaimTokenFormat());
method.param("pct", request.getPct());
method.param("rpt", request.getRptToken());
method.param("scope", request.getScope());
method.param("audience", request.getAudience());
method.param("subject_token", request.getSubjectToken());
if (permissions != null) {
for (Permission permission : permissions.getPermissions()) {
String resourceId = permission.getResourceId();
Set<String> scopes = permission.getScopes();
StringBuilder value = new StringBuilder();
View on GitHub (pinned to 66c7e15a37)
Solutions
- Before calling uma(), ensure AuthorizationRequest has a ticket: request.setTicket(permissionTicket) obtained from authzClient.protection().permission().create(resource, scopes), OR set permissions: request.setPermissions(new PermissionTicketToken(...)).
- Add a guard in your own code: if (request.getTicket() == null && request.getPermissions() == null) throw a clear domain error before calling the client.
- If using the resource-server-driven UMA flow, make sure you actually requested a ticket first and wired it into the request.
Example fix
// before
AuthorizationRequest request = new AuthorizationRequest();
String rpt = authzClient.authorization().request(rptToken).authorize(); // throws if ticket+permissions null
// after
AuthorizationRequest request = new AuthorizationRequest();
PermissionResponse ticketResp = authzClient.protection().permission()
.forResource(resourceId).create();
request.setTicket(ticketResp.getTicket());
String rpt = authzClient.authorization(request).authorize(); Defensive patterns
Strategy: validation
Validate before calling
// Validate the UMA request before handing it to the client
AuthorizationRequest req = ...;
if (req.getTicket() == null && req.getPermissions() == null) {
throw new IllegalArgumentException(
"UMA request needs a permission ticket or explicit permissions");
}
authzClient.authorization(req).authorize(); Try / catch
try {
authzClient.authorization(request).authorize();
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("permission ticket or the permissions")) {
// fix the request: obtain a ticket or set permissions, then retry
request.setTicket(obtainTicket());
authzClient.authorization(request).authorize();
} else throw e;
} Prevention
- Treat ticket/permissions as required fields in any builder/DTO wrapping AuthorizationRequest.
- Obtain the permission ticket from the Protection API in the same flow that uses it.
- Add a unit test asserting the precondition fires on an empty request.
When it happens
Trigger: Calling new AuthorizationRequest() and passing it to the uma(request) flow (or AuthzClient.authorization(...).authorize(request) with a UMA grant) without ever calling request.setTicket(...) or request.setPermissions(...). It occurs entirely client-side; no network round trip is attempted.
Common situations: Migrating from a ticket-based flow to a requesting-party flow and forgetting to populate permissions; building an AuthorizationRequest from a partial DTO/map; copy-paste where setTicket was removed but nothing replaced it; misunderstanding that UMA needs either the ticket (pushed by resource server) or explicit permissions.
Related errors
- Permission ticket must have a requester
- Permission ticket must have a resource
- Permission ticket must have a scope
- Permission request must not be null or empty
- Permission ticket must not be null or empty
AI-assisted analysis of keycloak/keycloak@66c7e15a37 (2026-08-14).
Data as JSON: /api/errors/9544d44544c0ff04.
Report an issue: GitHub.