Skip to content

Add support for per-client signing key selection for SAML and OIDC#47277

Open
wadahiro wants to merge 1 commit into
keycloak:mainfrom
openstandia:issue-10367
Open

Add support for per-client signing key selection for SAML and OIDC#47277
wadahiro wants to merge 1 commit into
keycloak:mainfrom
openstandia:issue-10367

Conversation

@wadahiro

@wadahiro wadahiro commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Closes #10367

The original issue (#10367) requests per-client signing key selection for SAML to support gradual key rotation. This PR extends the scope to OIDC as well, since the same need exists there — for example, relying parties that do not support JWKS URL and require manual key configuration, or environments where there is no network path from RP to OP (e.g., OIDC Implicit Flow with manually managed keys). In these cases, key rotation must be coordinated per-client, and a simultaneous switch is not feasible.

Supported signing points

OIDC: separate signing key per token type (matching the existing per-token-type algorithm
settings)

  • Access token
  • ID token / Logout token
  • UserInfo response
  • Authorization response (JARM)

SAML: single signing key applied to all signing points

  • Login response
  • Error response
  • Frontchannel logout request
  • Backchannel logout request
  • Logout response
  • Artifact response

If the configured key is not available (passive, disabled, or not found), it falls back to the
realm's active signing key.

Admin UI

  • Added new SigningKeySelect component shared by OIDC and SAML, enabling per-client signing key selection in client settings

Documentation

  • Added per-client signing key selection section in OIDC advanced settings and SAML client settings
  • Added gradual key rotation procedure covering both OIDC and SAML in realm keys documentation

Tests

  • Integration tests for all OIDC and SAML signing points, covering default key and client-specific key, fallback, and multi-client scenarios
  • Playwright tests for signing key selection UI

@keycloak-github-bot

Copy link
Copy Markdown

Unreported flaky test detected

If 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.model.session.UserSessionInitializerTest#testUserSessionPropagationBetweenSites

Keycloak CI - Store Model Tests

java.lang.AssertionError: 
threads didn't terminate in time: [main (RUNNABLE):
	at java.management@25.0.2/sun.management.ThreadImpl.dumpThreads0(Native Method)
	at java.management@25.0.2/sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:505)
	at java.management@25.0.2/sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:493)
...

Report flaky test

org.keycloak.testsuite.model.session.UserSessionProviderOfflineModelTest#testOfflineSessionLazyLoading

Keycloak CI - Store Model Tests

java.lang.AssertionError: 
threads didn't terminate in time: [main (RUNNABLE):
	at java.management@25.0.2/sun.management.ThreadImpl.dumpThreads0(Native Method)
	at java.management@25.0.2/sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:505)
	at java.management@25.0.2/sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:493)
...

Report flaky test

@keycloak-github-bot keycloak-github-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@keycloak-github-bot keycloak-github-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@keycloak-github-bot

Copy link
Copy Markdown

Unreported flaky test detected

If 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.model.singleUseObject.SingleUseObjectModelTest#testCluster

Keycloak CI - Store Model Tests

java.lang.AssertionError: 
threads didn't terminate in time: [main (RUNNABLE):
	at java.management@25.0.2/sun.management.ThreadImpl.dumpThreads0(Native Method)
	at java.management@25.0.2/sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:505)
	at java.management@25.0.2/sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:493)
...
org.infinispan.remoting.RemoteException: ISPN000217: Received exception from node-76, see cause for remote stack trace
	at org.infinispan.remoting.transport.ResponseCollectors.wrapRemoteException(ResponseCollectors.java:26)
	at org.infinispan.remoting.transport.ValidSingleResponseCollector.withException(ValidSingleResponseCollector.java:37)
	at org.infinispan.remoting.transport.ValidSingleResponseCollector.addResponse(ValidSingleResponseCollector.java:21)
	at org.infinispan.remoting.transport.impl.SingleTargetRequest.addResponse(SingleTargetRequest.java:69)
...

Report flaky test

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 37 changed files in this pull request and generated 5 comments.

Comment thread test-framework/saml/pom.xml
Comment thread js/apps/admin-ui/src/clients/advanced/FineGrainOpenIdConnect.tsx
Comment thread js/apps/admin-ui/test/clients/saml.spec.ts Outdated
Comment thread js/apps/admin-ui/test/clients/details.spec.ts Outdated
Comment thread test-framework/pom.xml
Copilot AI review requested due to automatic review settings July 18, 2026 06:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 18, 2026 12:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 25, 2026 08:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated 1 comment.

Comment thread js/apps/admin-ui/src/components/signing-key/SigningKeySelect.tsx Outdated
@keycloak-github-bot

Copy link
Copy Markdown

Unreported flaky test detected

If 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.forms.MultipleTabsLoginTest#multipleTabsParallelLoginTestWithAuthSessionExpiredAndRegisterClick

Keycloak CI - Forms IT (chrome)

org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertTrue.failNotTrue(AssertTrue.java:63)
	at org.junit.jupiter.api.AssertTrue.assertTrue(AssertTrue.java:36)
...

Report flaky test

@keycloak-github-bot keycloak-github-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

Copilot AI review requested due to automatic review settings July 25, 2026 10:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated 1 comment.

Comment thread js/apps/admin-ui/src/components/signing-key/SigningKeySelect.tsx Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 40 out of 40 changed files in this pull request and generated 1 comment.

Comment thread services/src/main/java/org/keycloak/protocol/saml/SamlService.java Outdated

@keycloak-github-bot keycloak-github-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@keycloak-github-bot

Copy link
Copy Markdown

Unreported flaky test detected

If 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.forms.MultipleTabsLoginTest#testLoginPageRefresh

Keycloak CI - Forms IT (chrome)

org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertTrue.failNotTrue(AssertTrue.java:63)
	at org.junit.jupiter.api.AssertTrue.assertTrue(AssertTrue.java:36)
...

Report flaky test

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 40 out of 40 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java:725

  • This Redirect-bound LogoutRequest puts the transformed XML KeyName into MessageSigningKeyId. When the client uses CERT_SUBJECT or NONE, the extension no longer identifies the selected KID, so key lookup/rotation can fail; the extension must carry keyPair.getKid() while the XML signature may still use the transformer.
                    KeyWrapper keyPair = getSigningKey(samlClient);
                    String keyName = samlClient.getXmlSigKeyInfoKeyNameTransformer().getKeyName(keyPair.getKid(), keyPair.getCertificate());

services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java:781

  • The final Redirect-bound LogoutResponse also reuses the transformed XML KeyName for MessageSigningKeyId. With CERT_SUBJECT or NONE, that extension cannot identify the configured signing KID; keep keyName for signWith, but put the raw keyPair.getKid() in the extension.
            String keyName = transformer.getKeyName(keyPair.getKid(), keyPair.getCertificate());
            binding.signatureAlgorithm(algorithm).signWith(keyName, (PrivateKey) keyPair.getPrivateKey(), (PublicKey) keyPair.getPublicKey(), keyPair.getCertificate()).signDocument();

Comment thread services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
Closes keycloak#10367

Signed-off-by: Hiroyuki Wada <h2-wada@nri.co.jp>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 40 out of 40 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Include some lower priority active keys to sign SAML response for specific clients

3 participants