8000 Enable configurable `client_id` parameter validation for federated client assertions by slaskawi · Pull Request #48026 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

Enable configurable client_id parameter validation for federated client assertions - #48026

Merged
mposolda merged 3 commits into
keycloak:mainfrom
slaskawi:48024/optional_client_id_for_signed_jwts
May 26, 2026
Merged

mposolda merged 3 commits into
keycloak:mainfrom
slaskawi:48024/optional_client_id_for_signed_jwts

Conversation

@slaskawi
Copy link
Copy Markdown
Contributor

Summary

This Pull Request introduces a configurable client_id validation for the Signed JWTs and disables it for Kubernetes Service Accounts.

As per RFC 7523, the Client ID for Client Assertions is optional.

Related tickets

Closes #48024

Relates to keycloak/terraform-provider-keycloak#1542
Relates to keycloak/terraform-provider-keycloak#1552

Copilot AI review requested due to automatic review settings April 14, 2026 07:33
@slaskawi
slaskawi requested a review from a team as a code owner April 14, 2026 07:33
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

Adds an opt-out for client_id form-parameter validation against the JWT sub when processing federated client assertions, and extends tests/utilities to send client_id alongside client_assertion when explicitly provided.

Changes:

  • Add isClientIdParamValidationEnabled() toggle (default enabled) to relax client_id vs sub validation when needed.
  • Thread a new clientIdParamValidationEnabled flag through FederatedJWTClientValidator instantiation (disabled for Kubernetes).
  • Extend Kubernetes client-auth tests and the HTTP request helper to cover requests that include client_id with client assertions.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AbstractHttpPostRequest.java Sends client_id with client assertions when explicitly set on the request builder.
tests/base/src/test/java/org/keycloak/tests/client/authentication/external/KubernetesClientAuthTest.java Adds coverage for Kubernetes federated assertions when client_id is present/mismatched.
services/src/main/java/org/keycloak/broker/spiffe/SpiffeIdentityProvider.java Updates validator construction for new flag (currently set to validate client_id).
services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java Updates validator construction for new flag (keeps validation enabled).
services/src/main/java/org/keycloak/broker/kubernetes/KubernetesIdentityProvider.java Disables client_id-param validation for Kubernetes assertions.
services/src/main/java/org/keycloak/authentication/authenticators/client/FederatedJWTClientValidator.java Adds constructor flag and overrides isClientIdParamValidationEnabled().
services/src/main/java/org/keycloak/authentication/authenticators/client/AbstractJWTClientValidator.java Gates client_id mismatch failure behind isClientIdParamValidationEnabled().

@slaskawi
Copy link
Copy Markdown
Contributor Author

Failures don't seem to be related

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 7 out of 7 changed files in this pull request and generated no new comments.

@mposolda mposolda 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.

@slaskawi Thanks! Can you please check my review comment inline?

@@ -103,8 +103,10 @@ private boolean validateClient() {

String clientIdParam = context.getHttpRequest().getDecodedFormParameters().getFirst(OAuth2Constants.CLIENT_ID);
if (clientIdParam != null && !clientIdParam.equals(clientId)) {
logger.debug("client_id parameter does not match JWT subject");
return failure("client_id parameter does not match sub claim");
if (isClientIdParamValidationEnabled()) {
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.

I suggest that instead of introducing isClientIdParamValidationEnabled() , we will check that:

  • if client_id parameter is provided, we always check that it matches with the clientId of the clientModel obtained from clientAssertionState (the client obtained on the line 115 by ClientModel client = clientAssertionState.getClient();)

  • So this means that this whole check can be moved below (after line 115).

  • I agree that it does not makes sense to always check that client_id parameter matches with the sub claim as this would be true just for JWT provided by private_key_jwt or client_secret_jwt client authentication, but it likely will not be true for the cases like spiffe (as per https://datatracker.ietf.org/doc/draft-ietf-oauth-spiffe-client-auth/ , the sub claim does not equal client_id, but it is something different and I suppose that for kubernetes it is the case as well).

However I think that in all cases, we can check if client_id (if sent) matches with the client identified by the authenticator. If it does not match, it is clear that something strange is going on as I don't see any case why client_id parameter would be different value than identified client?

If doing this, it will simplify this PR (as there will not be any changes needed to support isClientIdParamValidationEnabled() and send different values from different IDPs as well.)

WDYT?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks a million for taking the time to look into this!

I 100% agree with you! The simplified code indeed looks much better and cleaner.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@mposolda The PR has been updated and should be ready for re-review. Thanks!

@mposolda mposolda self-assigned this May 18, 2026
@slaskawi
slaskawi force-pushed the 48024/optional_client_id_for_signed_jwts branch from d0d8221 to 2ebc003 Compare May 23, 2026 13:25
Signed-off-by: Sebastian Łaskawiec <sebastian.laskawiec@defenseunicorns.com>
@slaskawi
slaskawi force-pushed the 48024/optional_client_id_for_signed_jwts branch from 2ebc003 to 48d28d3 Compare May 23, 2026 13:28
@slaskawi
Copy link
Copy Markdown
Contributor Author

Moving to draft to investigate failures.

@slaskawi
slaskawi marked this pull request as draft May 23, 2026 16:13
Signed-off-by: Sebastian Łaskawiec <sebastian.laskawiec@defenseunicorns.com>
@slaskawi
slaskawi force-pushed the 48024/optional_client_id_for_signed_jwts branch from 3456755 to d0e7834 Compare May 23, 2026 18:57
@slaskawi
slaskawi marked this pull request as ready for review May 25, 2026 09:26
@mposolda mposolda 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.

@slaskawi Thanks!

@mposolda
mposolda merged commit 3e8a131 into keycloak:main May 26, 2026
87 checks passed
@stianst stianst mentioned this pull request Jun 1, 2026
sashyo pushed a commit to tide-foundation/keycloak-IGA that referenced this pull request Jul 20, 2026
…ient assertions (keycloak#48026)

Closes keycloak#48024


Signed-off-by: Sebastian Łaskawiec <sebastian.laskawiec@defenseunicorns.com>
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.

Relax Client ID validation for the Signed JWT - Federated (Kubernetes Service Accounts)

4 participants

0