Token Exchange Delegation for Clients - #51306
Conversation
Unreported flaky test detectedIf the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR. org.keycloak.testsuite.adapter.servlet.SAMLServletAdapterTest#employeeSigPostNoIdpKeyTestCertSubjectAsKeyNameInKeyInfoKeycloak CI - Adapter IT Strict Cookies |
There was a problem hiding this comment.
Pull request overview
Adds consent-based client delegation to token exchange, producing scoped tokens with may_act and act actor tracing.
Changes:
- Adds client delegation scopes, validation, protocol mappers, and configuration.
- Adds Admin UI capability controls and consent messages.
- Adds integration coverage for delegation and revocation scenarios.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
themes/.../login/messages_en.properties |
Adds login consent text. |
themes/.../account/messages_en.properties |
Adds account consent text. |
tests/.../ClientDelegationTest.java |
Tests client delegation flows. |
services/.../org.keycloak.protocol.ProtocolMapper |
Registers the client mapper. |
services/.../ParameterizedScopeTypeProvider |
Registers the scope provider. |
services/.../TokenExchangeDelegationProvider.java |
Validates delegated client identity. |
services/.../ClientDelegationScopeType.java |
Defines client scope validation. |
services/.../OIDCLoginProtocolFactory.java |
Creates delegation scope and mappers. |
services/.../ParameterizedScopeUserPropertyMapper.java |
Makes user resolution extensible. |
services/.../ParameterizedScopeClientUserPropertyMapper.java |
Maps service-account properties. |
server-spi-private/.../OIDCConfigAttributes.java |
Adds the delegation attribute. |
js/.../useIsFeatureEnabled.ts |
Exposes the delegation feature. |
js/.../CapabilityConfig.tsx |
Adds the Admin UI toggle. |
js/.../messages_en.properties |
Adds Admin UI labels and help. |
48511ab to
c2dbecc
Compare
|
Slightly updated the demo app: Screencast.From.2026-07-31.15-21-48.mp4 |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Suppressed comments (2)
tests/base/src/test/java/org/keycloak/tests/oauth/tokenexchange/ClientDelegationTest.java:446
- The success path authenticates the exchange as
test-appand this static mapper makes the actor token valid for that client, so the tests still pass if the new dynamic audience mapper never addsagent-appto the subject token. Cover the advertised mapper-free flow by enabling standard exchange onagent-app, authenticating the exchange asagent-app, removing this mapper, and asserting the subject token audience.
ProtocolMapperRepresentation audienceMapper = new ProtocolMapperRepresentation();
services/src/main/java/org/keycloak/protocol/oidc/mappers/ParameterizedScopeClientSubMapper.java:22
- This mapper is registered as an Admin UI-selectable protocol mapper, but its configuration exposes no token-claim name or JSON type. A mapper created through the UI therefore reaches
mapClaimwithoutclaim.nameand silently emits nothing; expose the standard attribute configuration as the base parameterized mapper does.
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, ParameterizedScopeClientSubMapper.class);
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
themes/src/main/resources/theme/base/login/messages/messages_en.properties:128
- The base login template calls
advancedMsgwithout the parameter and appends the parameter separately (themes/.../base/login/login-oauth-grant.ftl:24), so this renders literally asAllow {0} to act on your behalf?: agent-app. Pass the parameter toadvancedMsgas the keycloak.v2 template does.
clientDelegationScopeConsentText=Allow {0} to act on your behalf?
js/apps/admin-ui/src/clients/add/CapabilityConfig.tsx:505
- Enabling this switch leaves
serviceAccountsEnabledfalse by default, whileClientDelegationScopeTyperejects targets without service accounts; the UI can therefore show delegation as enabled although every request silently drops the scope. Mirror the authorization switch behavior by enabling the service account when delegation is turned on.
<DefaultSwitchControl
name={convertAttributeNameToForm<FormFields>(
"attributes.client.delegation.enabled",
)}
label={t("clientDelegationEnabled")}
labelIcon={t("clientDelegationEnabledHelp")}
stringify
/>
Closes keycloak#51305 Signed-off-by: Martin Bartoš <mabartos@redhat.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java:415
- This only creates
delegation:clientwhile a realm is being created/imported; enabling the feature for an existing realm never runs this path, so the advertised realm-default scope is absent and both existing and subsequently created clients cannot request it without manual setup. Add an existing-realm bootstrap/migration that creates the scope and registers it as an optional default.
ClientScopeModel clientDelegationScope = newRealm.addClientScope(CLIENT_DELEGATION_SCOPE);
| ClientModel client = userSession.getRealm().getClientByClientId(parameterValue); | ||
| if (client == null) { | ||
| continue; | ||
| } | ||
| UserModel serviceAccount = keycloakSession.users().getServiceAccount(client); | ||
| if (serviceAccount == null) { | ||
| continue; | ||
| } | ||
| resolvedValues.add(serviceAccount.getId()); |
There was a problem hiding this comment.
It's related also to the "delegation:" user-to-user delegation.
Created a follow-up issue: #51398
Unreported flaky test detectedIf the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR. org.keycloak.testsuite.broker.KcOidcBrokerTest#loginWithExistingUserWithBruteForceEnabledKeycloak CI - Java Distribution IT (windows-latest - temurin - 21) |
Pros/Cons of the previous and current approach
delegation:service-account-<client>(old)Pros
delegation:<username>scope infrastructure — no new scope type, mapper, or validation logic neededCons
service-account-*username patternimpersonationrole to the service account, which opens the Admin REST API impersonation endpointmay_actonly containssub, no explicit client binding for better actor identificationsubject-appper actor client - manual setup that must be repeated for every new delegation targetdelegation:client:<client>(new)Pros
client_iddirectly - no internal naming conventions exposedclient.delegation.enabledtoggle + user consentmay_actcontains bothsubandclient_idfor better traceabilityCons