diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientAssertionState.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientAssertionState.java index f9c1840be6c..ba99d5a2f91 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientAssertionState.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientAssertionState.java @@ -21,8 +21,7 @@ public class ClientAssertionState { private final JWSInput jws; private final JsonWebToken token; - public ClientAssertionState(ClientModel client, String clientAssertionType, String clientAssertion, JWSInput jws, JsonWebToken token) { - this.client = client; + public ClientAssertionState(String clientAssertionType, String clientAssertion, JWSInput jws, JsonWebToken token) { this.clientAssertionType = clientAssertionType; this.clientAssertion = clientAssertion; this.jws = jws; @@ -69,8 +68,6 @@ public class ClientAssertionState { JWSInput jws = null; JsonWebToken token = null; - ClientModel client = null; - if (clientAssertion != null) { jws = new JWSInput(clientAssertion); token = jws.readJsonContent(JsonWebToken.class); @@ -79,13 +76,9 @@ public class ClientAssertionState { event.detail(Details.CLIENT_ASSERTION_ID, token.getId()); event.detail(Details.CLIENT_ASSERTION_ISSUER, token.getIssuer()); event.detail(Details.CLIENT_ASSERTION_SUB, token.getSubject()); - - if (token.getSubject() != null) { - client = context.getRealm().getClientByClientId(token.getSubject()); - } } - return new ClientAssertionState(client, clientAssertionType, clientAssertion, jws, token); + return new ClientAssertionState(clientAssertionType, clientAssertion, jws, token); } } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/FederatedJWTClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/FederatedJWTClientAuthenticator.java index c548a9e2783..3ef2177c9c3 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/FederatedJWTClientAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/FederatedJWTClientAuthenticator.java @@ -84,6 +84,11 @@ public class FederatedJWTClientAuthenticator extends AbstractClientAuthenticator return; } + // Ignore for self-signed client assertions + if (Objects.equals(clientAssertionState.getToken().getIssuer(), clientAssertionState.getToken().getSubject())) { + return; + } + ClientAssertionIdentityProviderFactory.ClientAssertionStrategy strategy = findStrategy(clientAssertionState.getClientAssertionType()); if (strategy == null) { return; diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java index 9eeadd2ffcd..a54090f68e8 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java @@ -25,6 +25,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import jakarta.ws.rs.core.Response; @@ -64,7 +65,20 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator { @Override public void authenticateClient(ClientAuthenticationFlowContext context) { try { + ClientAssertionState clientAssertionState = context.getState(ClientAssertionState.class, ClientAssertionState.supplier()); + JsonWebToken jwt = clientAssertionState.getToken(); + + // Ignore for client assertions signed by third-parties + if (!Objects.equals(jwt.getIssuer(), jwt.getSubject())) { + return; + } + + if (clientAssertionState.getClient() == null) { + clientAssertionState.setClient(context.getRealm().getClientByClientId(jwt.getSubject())); + } + JWTClientValidator validator = new JWTClientValidator(context, this::verifySignature, getId()); + if (!validator.validate()) return; context.success(); diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientSecretAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientSecretAuthenticator.java index f11fec8baef..dad5e3b7d71 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientSecretAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientSecretAuthenticator.java @@ -22,6 +22,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import jakarta.ws.rs.core.Response; @@ -55,6 +56,18 @@ public class JWTClientSecretAuthenticator extends AbstractClientAuthenticator { @Override public void authenticateClient(ClientAuthenticationFlowContext context) { try { + ClientAssertionState clientAssertionState = context.getState(ClientAssertionState.class, ClientAssertionState.supplier()); + JsonWebToken jwt = clientAssertionState.getToken(); + + // Ignore for client assertions signed by third-parties + if (!Objects.equals(jwt.getIssuer(), jwt.getSubject())) { + return; + } + + if (clientAssertionState.getClient() == null) { + clientAssertionState.setClient(context.getRealm().getClientByClientId(jwt.getSubject())); + } + JWTClientValidator validator = new JWTClientValidator(context, this::verifySignature, getId()); if (!validator.validate()) return; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/JWTAuthorizationGrantValidator.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/JWTAuthorizationGrantValidator.java index 3ac34cc4597..e040d03fb41 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/JWTAuthorizationGrantValidator.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/JWTAuthorizationGrantValidator.java @@ -51,7 +51,8 @@ public class JWTAuthorizationGrantValidator extends AbstractBaseJWTValidator imp try { JWSInput jws = new JWSInput(assertion); JsonWebToken jwt = jws.readJsonContent(JsonWebToken.class); - ClientAssertionState clientAssertionState = new ClientAssertionState(client, OAuth2Constants.JWT_AUTHORIZATION_GRANT, assertion, jws, jwt); + ClientAssertionState clientAssertionState = new ClientAssertionState(OAuth2Constants.JWT_AUTHORIZATION_GRANT, assertion, jws, jwt); + clientAssertionState.setClient(client); return new JWTAuthorizationGrantValidator(session, scope, clientAssertionState); } catch (JWSInputException e) { throw new RuntimeException("The provided assertion is not a valid JWT");