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

  1. 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(...)).
  2. Add a guard in your own code: if (request.getTicket() == null && request.getPermissions() == null) throw a clear domain error before calling the client.
  3. 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

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


AI-assisted analysis of keycloak/keycloak@66c7e15a37 (2026-08-14). Data as JSON: /api/errors/9544d44544c0ff04. Report an issue: GitHub.