KeycloakIdentity should support creating a user-model-bound identity for server-side authorization evaluation · keycloak keycloak · Discussion #46661 · GitHub
You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
8000
Repository navigation
KeycloakIdentity should support creating a user-model-bound identity for server-side authorization evaluation
#46661
I'm developing a Keycloak extension (realm resource provider) that performs programmatic permission evaluation for resources protected by role-based policies. The extension receives a bearer token, constructs a KeycloakIdentity, and uses AuthorizationProvider.evaluators() to evaluate permissions, following the standard pattern.
Problem
When a resource is protected by a role-based policy referencing a client role defined in a different client than the resource server, KeycloakIdentity may produce incorrect authorization decisions.
Scenario:
A resource on client-b is protected by a policy requiring a client role from client-x
User has the client-x role assigned
User's token (regardless of how it was obtained, direct login or token exchange) may not include client-x roles in resource_access, depending on client-b's scope/audience configuration
new KeycloakIdentity(session, token) builds role attributes exclusively from the token's resource_access
Since client-x roles are not present in the token, kc.client.client-x.roles attribute is missing from the identity
RolePolicyProvider cannot find the role → DENY
Meanwhile, the Keycloak admin console (Authorization > Evaluate) correctly returns PERMIT for the same user, resource, and policy.
Note: Token exchange exacerbates this issue (the exchanged token's role set is determined by the target client's scope configuration), but the core problem exists regardless of how the token was obtained. Any token whose scope configuration does not map cross-client roles will trigger this behavior.
Root Cause
KeycloakIdentity constructors are token-bound. Role attributes are derived from accessToken.getRealmAccess() and accessToken.getResourceAccess(), which are subject to OIDC scope filtering.
However, PolicyEvaluationService.createIdentity() (used by the admin console) compensates for this. After creating a token via TokenManager.createClientAccessToken(), it additionally reads all user roles from the user model and injects them into the token (source):
This ensures the identity includes all user roles regardless of token scope constraints. But this logic is internal to PolicyEvaluationService and not exposed as a public API.
The Gap
Approach
Role Source
Scope Filtered
Available To
new KeycloakIdentity(session, token)
Token's resource_access
Yes
Public API, extensions
PolicyEvaluationService.createIdentity()
Token + UserModel.getRoleMappingsStream()
No (enriched)
Admin console only (internal)
There is no public API for extensions to create a user-model-bound identity that includes all role assignments. This forces extension developers to replicate the same enrichment logic that PolicyEvaluationService already implements internally.
Proposal
Provide a public factory method on KeycloakIdentity (or a utility class) that creates an identity enriched with all user roles:
// Resolves user from the session's bearer token subject // primary use case for extensions evaluating permissions// for the authenticated userIdentityidentity = KeycloakIdentity.fromUser(session, realm);
// Explicit userId overload for scenarios where permissions are// evaluated on behalf of a different user (e.g., admin tooling,// delegated evaluation), matching PolicyEvaluationService's// current internal behaviorIdentityidentity = KeycloakIdentity.fromUser(session, realm, userId);
This would expose the same identity construction logic that PolicyEvaluationService already uses internally, enabling extension developers to perform authorization evaluation consistent with Keycloak's own admin console.
Why This Matters
Inconsistency: Keycloak's own admin console compensates for the token scope limitation in PolicyEvaluationService, but this compensation is not available to extensions through any public API
Cross-client policies are a valid use case: Organizations define client roles in dedicated clients and reference them in policies across multiple resource servers
The problem is token-agnostic: Whether the token comes from direct authentication or token exchange, if the client's scope configuration does not map cross-client roles, the identity will be incomplete
No clean workaround exists: Extensions must replicate the exact same UserModel.getRoleMappingsStream() enrichment logic found in PolicyEvaluationService, duplicating internal Keycloak behavior
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Description
Context
I'm developing a Keycloak extension (realm resource provider) that performs programmatic permission evaluation for resources protected by role-based policies. The extension receives a bearer token, constructs a KeycloakIdentity, and uses
AuthorizationProvider.evaluators()to evaluate permissions, following the standard pattern.Problem
When a resource is protected by a role-based policy referencing a client role defined in a different client than the resource server, KeycloakIdentity may produce incorrect authorization decisions.
Scenario:
new KeycloakIdentity(session, token)builds role attributes exclusively from the token's resource_accesskc.client.client-x.rolesattribute is missing from the identityRolePolicyProvidercannot find the role → DENYMeanwhile, the Keycloak admin console (Authorization > Evaluate) correctly returns PERMIT for the same user, resource, and policy.
Root Cause
KeycloakIdentity constructors are token-bound. Role attributes are derived from
accessToken.getRealmAccess()andaccessToken.getResourceAccess(), which are subject to OIDC scope filtering.However,
PolicyEvaluationService.createIdentity()(used by the admin console) compensates for this. After creating a token viaTokenManager.createClientAccessToken(), it additionally reads all user roles from the user model and injects them into the token (source):This ensures the identity includes all user roles regardless of token scope constraints. But this logic is internal to PolicyEvaluationService and not exposed as a public API.
The Gap
There is no public API for extensions to create a user-model-bound identity that includes all role assignments. This forces extension developers to replicate the same enrichment logic that PolicyEvaluationService already implements internally.
Proposal
Provide a public factory method on KeycloakIdentity (or a utility class) that creates an identity enriched with all user roles:
This would expose the same identity construction logic that
PolicyEvaluationServicealready uses internally, enabling extension developers to perform authorization evaluation consistent with Keycloak's own admin console.Why This Matters
UserModel.getRoleMappingsStream()enrichment logic found in PolicyEvaluationService, duplicating internal Keycloak behaviorSources:
All reactions