diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/AbstractJWTClientValidator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/AbstractJWTClientValidator.java index c3bda51276d..d75a02621d9 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/AbstractJWTClientValidator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/AbstractJWTClientValidator.java @@ -101,12 +101,6 @@ public abstract class AbstractJWTClientValidator extends AbstractBaseJWTValidato return failure("Token sub claim is required"); } - 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"); - } - String expectedTokenIssuer = getExpectedTokenIssuer(); if (expectedTokenIssuer != null && !expectedTokenIssuer.equals(token.getIssuer())) { return false; @@ -116,11 +110,16 @@ public abstract class AbstractJWTClientValidator extends AbstractBaseJWTValidato if (client == null) { return failure(AuthenticationFlowError.CLIENT_NOT_FOUND); - } else { - context.getEvent().client(client.getClientId()); - context.setClient(client); } + String clientIdParam = context.getHttpRequest().getDecodedFormParameters().getFirst(OAuth2Constants.CLIENT_ID); + if (clientIdParam != null && !clientIdParam.equals(client.getClientId())) { + return failure("client_id parameter does not match authenticated client"); + } + + context.getEvent().client(client.getClientId()); + context.setClient(client); + if (!client.isEnabled()) { return failure(AuthenticationFlowError.CLIENT_DISABLED); } diff --git a/tests/base/src/test/java/org/keycloak/tests/client/authentication/external/KubernetesClientAuthTest.java b/tests/base/src/test/java/org/keycloak/tests/client/authentication/external/KubernetesClientAuthTest.java index 158322c6993..fb88d5ba530 100644 --- a/tests/base/src/test/java/org/keycloak/tests/client/authentication/external/KubernetesClientAuthTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/client/authentication/external/KubernetesClientAuthTest.java @@ -18,6 +18,7 @@ import org.keycloak.testframework.realm.IdentityProviderBuilder; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RealmBuilder; import org.keycloak.testframework.realm.RealmConfig; +import org.keycloak.testsuite.util.oauth.AccessTokenResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.MethodOrderer; @@ -72,6 +73,30 @@ public class KubernetesClientAuthTest extends AbstractBaseClientAuthTest { assertSuccess(internalClientId, jwt.getId(), expectedTokenIssuer, externalClientId, events.poll()); } + @Test + public void testValidTokenWithClientId() { + JsonWebToken jwt = createDefaultToken(); + String jws = getIdentityProvider().encodeToken(jwt); + AccessTokenResponse response = oAuthClient.clientCredentialsGrantRequest() + .client(INTERNAL_CLIENT_ID) + .clientJwt(jws, getClientAssertionType()) + .send(); + assertSuccess(internalClientId, response); + assertSuccess(internalClientId, jwt.getId(), expectedTokenIssuer, externalClientId, events.poll()); + } + + @Test + public void testWrongClientIdFailsValidation() { + JsonWebToken jwt = createDefaultToken(); + String jws = getIdentityProvider().encodeToken(jwt); + AccessTokenResponse response = oAuthClient.clientCredentialsGrantRequest() + .client("completely-wrong-id") + .clientJwt(jws, getClientAssertionType()) + .send(); + assertFailure(response); + assertFailure("completely-wrong-id", expectedTokenIssuer, externalClientId, jwt.getId(), "client_not_found", events.poll()); + } + @Test public void testReuse() { JsonWebToken jwt = createDefaultToken(); diff --git a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AbstractHttpPostRequest.java b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AbstractHttpPostRequest.java index 6f3e0c947c2..73c5a8cd52c 100644 --- a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AbstractHttpPostRequest.java +++ b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AbstractHttpPostRequest.java @@ -148,6 +148,9 @@ public abstract class AbstractHttpPostRequest { if (clientAssertion != null && clientAssertionType != null) { parameter("client_assertion_type", clientAssertionType); parameter("client_assertion", clientAssertion); + if (this.clientId != null) { + parameter("client_id", this.clientId); + } } else if (tokenType != null && tokenValue != null) { header("Authorization", tokenType + " " + tokenValue); } else if (clientSecret != null) {