From a8418b251d177e280c474a6a348f8bb6f4a4dd75 Mon Sep 17 00:00:00 2001 From: Giuseppe Graziano Date: Thu, 29 Jan 2026 17:20:51 +0100 Subject: [PATCH] Unique issuer for identity providers Closes #45747 Signed-off-by: Giuseppe Graziano --- ...horizationGrantIdentityProviderConfig.java | 5 +- .../KubernetesIdentityProviderConfig.java | 27 +-- .../broker/oidc/IssuerValidation.java | 41 +++++ .../oidc/OIDCIdentityProviderConfig.java | 5 +- .../google/GoogleIdentityProviderConfig.java | 6 +- .../IdentityProviderIssuerTest.java | 168 ++++++++++++++++++ .../IdentityProviderKubernetesTest.java | 4 +- .../IdentityProviderOidcTest.java | 1 + .../FederatedClientAuthConflictsTest.java | 16 +- 9 files changed, 239 insertions(+), 34 deletions(-) create mode 100644 services/src/main/java/org/keycloak/broker/oidc/IssuerValidation.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderIssuerTest.java diff --git a/services/src/main/java/org/keycloak/broker/jwtauthorizationgrant/JWTAuthorizationGrantIdentityProviderConfig.java b/services/src/main/java/org/keycloak/broker/jwtauthorizationgrant/JWTAuthorizationGrantIdentityProviderConfig.java index 53be9f9f640..aea328d8251 100644 --- a/services/src/main/java/org/keycloak/broker/jwtauthorizationgrant/JWTAuthorizationGrantIdentityProviderConfig.java +++ b/services/src/main/java/org/keycloak/broker/jwtauthorizationgrant/JWTAuthorizationGrantIdentityProviderConfig.java @@ -1,12 +1,13 @@ package org.keycloak.broker.jwtauthorizationgrant; +import org.keycloak.broker.oidc.IssuerValidation; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.RealmModel; import static org.keycloak.broker.oidc.OIDCIdentityProviderConfig.JWKS_URL; import static org.keycloak.common.util.UriUtils.checkUrl; -public class JWTAuthorizationGrantIdentityProviderConfig extends IdentityProviderModel implements JWTAuthorizationGrantConfig { +public class JWTAuthorizationGrantIdentityProviderConfig extends IdentityProviderModel implements JWTAuthorizationGrantConfig, IssuerValidation { public JWTAuthorizationGrantIdentityProviderConfig() { } @@ -17,7 +18,7 @@ public class JWTAuthorizationGrantIdentityProviderConfig extends IdentityProvide @Override public void validate(RealmModel realm) { - checkUrl(realm.getSslRequired(), getIssuer(), ISSUER); checkUrl(realm.getSslRequired(), getJwksUrl(), JWKS_URL); + validateIssuer(realm); } } diff --git a/services/src/main/java/org/keycloak/broker/kubernetes/KubernetesIdentityProviderConfig.java b/services/src/main/java/org/keycloak/broker/kubernetes/KubernetesIdentityProviderConfig.java index 1fad20b055d..d3f32896443 100644 --- a/services/src/main/java/org/keycloak/broker/kubernetes/KubernetesIdentityProviderConfig.java +++ b/services/src/main/java/org/keycloak/broker/kubernetes/KubernetesIdentityProviderConfig.java @@ -1,20 +1,12 @@ package org.keycloak.broker.kubernetes; -import java.util.Objects; -import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; -import org.keycloak.cache.AlternativeLookupProvider; +import org.keycloak.broker.oidc.IssuerValidation; import org.keycloak.models.IdentityProviderModel; -import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.util.Strings; -import org.keycloak.utils.KeycloakSessionUtil; -import static org.keycloak.common.util.UriUtils.checkUrl; -public class KubernetesIdentityProviderConfig extends IdentityProviderModel { - - public static final String ISSUER = OIDCIdentityProviderConfig.ISSUER; +public class KubernetesIdentityProviderConfig extends IdentityProviderModel implements IssuerValidation { public KubernetesIdentityProviderConfig() { } @@ -48,19 +40,6 @@ public class KubernetesIdentityProviderConfig extends IdentityProviderModel { @Override public void validate(RealmModel realm) { super.validate(realm); - - String issuer = getIssuer(); - if (Strings.isEmpty(issuer)) { - throw new IllegalArgumentException(ISSUER + " is required"); - } - checkUrl(realm.getSslRequired(), issuer, ISSUER); - - KeycloakSession session = KeycloakSessionUtil.getKeycloakSession(); - AlternativeLookupProvider lookupProvider = session.getProvider(AlternativeLookupProvider.class); - IdentityProviderModel existingIdp = lookupProvider.lookupIdentityProviderFromIssuer(session, getIssuer()); - if (existingIdp != null && (getInternalId() == null || !Objects.equals(existingIdp.getInternalId(), getInternalId()))) { - throw new IllegalArgumentException("Issuer URL already used for IDP '" + existingIdp.getAlias() + "'"); - } - + validateIssuer(realm); } } diff --git a/services/src/main/java/org/keycloak/broker/oidc/IssuerValidation.java b/services/src/main/java/org/keycloak/broker/oidc/IssuerValidation.java new file mode 100644 index 00000000000..7ef6b12a8c9 --- /dev/null +++ b/services/src/main/java/org/keycloak/broker/oidc/IssuerValidation.java @@ -0,0 +1,41 @@ +package org.keycloak.broker.oidc; + +import java.util.Map; +import java.util.Objects; + +import org.keycloak.cache.AlternativeLookupProvider; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.util.Strings; +import org.keycloak.utils.KeycloakSessionUtil; + +import static org.keycloak.common.util.UriUtils.checkUrl; +import static org.keycloak.models.IdentityProviderModel.ISSUER; + +public interface IssuerValidation { + + Map getConfig(); + + String getInternalId(); + + default void validateIssuer(RealmModel realm) { + + String issuer = getConfig().get(ISSUER); + if (Strings.isEmpty(issuer)) { + throw new IllegalArgumentException("Issuer is required"); + } + + checkUrl(realm.getSslRequired(), issuer, "Issuer"); + + KeycloakSession session = KeycloakSessionUtil.getKeycloakSession(); + AlternativeLookupProvider lookupProvider = session.getProvider(AlternativeLookupProvider.class); + + if (lookupProvider != null) { + IdentityProviderModel existingIdp = lookupProvider.lookupIdentityProviderFromIssuer(session, getConfig().get(ISSUER)); + if (existingIdp != null && (getInternalId() == null || !Objects.equals(existingIdp.getInternalId(), getInternalId()))) { + throw new IllegalArgumentException("Issuer URL already used for IDP '" + existingIdp.getAlias() + "', Issuer must be unique if the idp supports JWT Authorization Grant or Federated Client Authentication"); + } + } + } +} diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java index 42f7ed0aa78..1c6312ae248 100755 --- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java +++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java @@ -26,7 +26,7 @@ import static org.keycloak.common.util.UriUtils.checkUrl; /** * @author Pedro Igor */ -public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig implements JWTAuthorizationGrantConfig { +public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig implements JWTAuthorizationGrantConfig, IssuerValidation { public static final String JWKS_URL = "jwksUrl"; @@ -181,5 +181,8 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig imp throw new IllegalArgumentException(String.format("The 'Validating public key' is required when '%s' enabled and 'Use JWKS URL' disabled", optionText)); } } + if (isJWTAuthorizationGrantEnabled() || isSupportsClientAssertions()) { + validateIssuer(realm); + } } } diff --git a/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderConfig.java b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderConfig.java index 9af1cebda89..549b9fb6b9c 100644 --- a/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderConfig.java +++ b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderConfig.java @@ -17,6 +17,7 @@ package org.keycloak.social.google; import org.keycloak.broker.jwtauthorizationgrant.JWTAuthorizationGrantConfig; +import org.keycloak.broker.oidc.IssuerValidation; import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.RealmModel; @@ -24,7 +25,7 @@ import org.keycloak.models.RealmModel; /** * @author Vlastimil Elias (velias at redhat dot com) */ -public class GoogleIdentityProviderConfig extends OIDCIdentityProviderConfig implements JWTAuthorizationGrantConfig { +public class GoogleIdentityProviderConfig extends OIDCIdentityProviderConfig implements JWTAuthorizationGrantConfig, IssuerValidation { public GoogleIdentityProviderConfig(IdentityProviderModel model) { super(model); @@ -72,5 +73,8 @@ public class GoogleIdentityProviderConfig extends OIDCIdentityProviderConfig imp if (!GoogleIdentityProvider.ISSUER_URL.equals(getConfig().get(ISSUER))) { throw new IllegalArgumentException("The issuer url [" + getConfig().get(ISSUER) + "] is invalid"); } + if (isJWTAuthorizationGrantEnabled()) { + validateIssuer(realm); + } } } diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderIssuerTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderIssuerTest.java new file mode 100644 index 00000000000..565de8d0bf4 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderIssuerTest.java @@ -0,0 +1,168 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.admin.identityprovider; + +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; + +import org.keycloak.admin.client.resource.IdentityProviderResource; +import org.keycloak.broker.jwtauthorizationgrant.JWTAuthorizationGrantIdentityProviderFactory; +import org.keycloak.broker.kubernetes.KubernetesIdentityProviderFactory; +import org.keycloak.broker.oidc.OIDCIdentityProviderFactory; +import org.keycloak.common.Profile; +import org.keycloak.representations.idm.ErrorRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.social.google.GoogleIdentityProviderFactory; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.server.KeycloakServerConfig; +import org.keycloak.testframework.server.KeycloakServerConfigBuilder; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@KeycloakIntegrationTest(config = IdentityProviderIssuerTest.TestServerConfig.class) +public class IdentityProviderIssuerTest extends AbstractIdentityProviderTest { + + @Test + public void testCreateUpdateDuplicateIdentityProvider() { + String issuer = "http://localhost:8080"; + + // JWTAuthorizationGrant idp - JWTAuthorizationGrant idp: not allowed + testCreateIdentityProviderDuplicateNotAllowed(JWTAuthorizationGrantIdentityProviderFactory.PROVIDER_ID, JWTAuthorizationGrantIdentityProviderFactory.PROVIDER_ID, issuer); + + // Kubernetes idp - Kubernetes idp: not allowed + testCreateIdentityProviderDuplicateNotAllowed(KubernetesIdentityProviderFactory.PROVIDER_ID, KubernetesIdentityProviderFactory.PROVIDER_ID, issuer); + + // JWTAuthorizationGrant idp - Kubernetes idp: not allowed + testCreateIdentityProviderDuplicateNotAllowed(JWTAuthorizationGrantIdentityProviderFactory.PROVIDER_ID, KubernetesIdentityProviderFactory.PROVIDER_ID, issuer); + + // JWTAuthorizationGrant idp - OIDC idp: allowed + testCreateIdentityProviderDuplicateAllowed(JWTAuthorizationGrantIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID, issuer, false, false); + + // JWTAuthorizationGrant idp - OIDC idp: not allowed + testCreateIdentityProviderDuplicateNotAllowed(JWTAuthorizationGrantIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID, issuer, true, false); + + // Kubernetes idp - OIDC idp: not allowed + testCreateIdentityProviderDuplicateNotAllowed(KubernetesIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID, issuer, false, true); + + // OIDC idp - OIDC idp: allowed + testCreateIdentityProviderDuplicateAllowed(OIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID, issuer, false, false); + + // OIDC idp - OIDC idp: not allowed + testCreateIdentityProviderDuplicateNotAllowed(OIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID, issuer, true, false); + testCreateIdentityProviderDuplicateNotAllowed(OIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID, issuer, false, true); + + // Google idp - Google idp: allowed + testCreateIdentityProviderDuplicateAllowed(GoogleIdentityProviderFactory.PROVIDER_ID, GoogleIdentityProviderFactory.PROVIDER_ID, null, false, false); + + // Google idp - Google idp: allowed + testCreateIdentityProviderDuplicateAllowed(GoogleIdentityProviderFactory.PROVIDER_ID, GoogleIdentityProviderFactory.PROVIDER_ID, null, false, true); + + // Google idp - Google idp: not allowed + testCreateIdentityProviderDuplicateNotAllowed(GoogleIdentityProviderFactory.PROVIDER_ID, GoogleIdentityProviderFactory.PROVIDER_ID, null, true, false); + } + + public void testCreateIdentityProviderDuplicateNotAllowed(String providerId1, String providerId2, String issuer) { + testCreateIdentityProviderDuplicateAllowed(providerId1, providerId2, issuer, false, false, false); + } + + public void testCreateIdentityProviderDuplicateNotAllowed(String providerId1, String providerId2, String issuer, boolean JWTAuthorizationGrantEnabled, boolean federatedAuthenticationEnabled) { + testCreateIdentityProviderDuplicateAllowed(providerId1, providerId2, issuer, JWTAuthorizationGrantEnabled, federatedAuthenticationEnabled, false); + } + + public void testCreateIdentityProviderDuplicateAllowed(String providerId1, String providerId2, String issuer, boolean JWTAuthorizationGrantEnabled, boolean federatedAuthenticationEnabled) { + testCreateIdentityProviderDuplicateAllowed(providerId1, providerId2, issuer, JWTAuthorizationGrantEnabled, federatedAuthenticationEnabled, true); + } + + public void testCreateIdentityProviderDuplicateAllowed(String providerId1, String providerId2, String issuer, boolean JWTAuthorizationGrantEnabled, boolean federatedAuthenticationEnabled, boolean allowDuplicate) { + String idp1 = "idp1"; + String idp2 = "idp2"; + IdentityProviderRepresentation identityProvider1 = createRep(idp1, providerId1, issuer, JWTAuthorizationGrantEnabled, federatedAuthenticationEnabled); + IdentityProviderRepresentation identityProvider2 = createRep(idp2, providerId2, issuer, JWTAuthorizationGrantEnabled, federatedAuthenticationEnabled); + + try (Response response = managedRealm.admin().identityProviders().create(identityProvider1)) { + Assertions.assertEquals(201, response.getStatus()); + } + + managedRealm.cleanup().add(r -> r.identityProviders().get(idp1).remove()); + + Response response = managedRealm.admin().identityProviders().create(identityProvider2); + if (allowDuplicate) { + Assertions.assertEquals(201, response.getStatus()); + managedRealm.cleanup().add(r -> r.identityProviders().get(idp2).remove()); + } else { + Assertions.assertEquals(400, response.getStatus()); + ErrorRepresentation error = response.readEntity(ErrorRepresentation.class); + assertEquals("Issuer URL already used for IDP '" + idp1 + "', Issuer must be unique if the idp supports JWT Authorization Grant or Federated Client Authentication", error.getErrorMessage()); + + + //create with different issuer only if issuer is present + if (issuer != null) { + identityProvider2.getConfig().put("issuer", "https://localhost2"); + response = managedRealm.admin().identityProviders().create(identityProvider2); + Assertions.assertEquals(201, response.getStatus()); + + managedRealm.cleanup().add(r -> r.identityProviders().get(idp2).remove()); + + IdentityProviderResource idpResource = managedRealm.admin().identityProviders().get(idp2); + identityProvider2 = idpResource.toRepresentation(); + identityProvider2.getConfig().put("issuer", issuer); + + try { + idpResource.update(identityProvider2); + Assertions.fail("Duplicate issuer URL not detected"); + } catch (WebApplicationException ex) { + Assertions.assertEquals(400, ex.getResponse().getStatus()); + error = ex.getResponse().readEntity(ErrorRepresentation.class); + assertEquals("Issuer URL already used for IDP '" + idp1 + "', Issuer must be unique if the idp supports JWT Authorization Grant or Federated Client Authentication", error.getErrorMessage()); + } + } + } + + managedRealm.runCleanup(); + } + + public IdentityProviderRepresentation createRep(String alias, String providerId, String issuer, boolean JWTAuthorizationGrantEnabled, boolean federatedAuthenticationEnabled) { + IdentityProviderRepresentation identityProvider = createRep(alias, providerId); + + // Use the passed issuer if not null, otherwise default to localhost if not Google/Social + if (issuer != null) { + identityProvider.getConfig().put("issuer", issuer); + } else if (!providerId.equals(GoogleIdentityProviderFactory.PROVIDER_ID)) { + // Default for generic tests if null passed + identityProvider.getConfig().put("issuer", issuer); + } + + identityProvider.getConfig().put("jwtAuthorizationGrantEnabled", String.valueOf(JWTAuthorizationGrantEnabled)); + identityProvider.getConfig().put("supportsClientAssertions", String.valueOf(federatedAuthenticationEnabled)); + + identityProvider.getConfig().put("useJwksUrl", "true"); + identityProvider.getConfig().put("jwksUrl", issuer); + + return identityProvider; + } + + public static class TestServerConfig implements KeycloakServerConfig { + @Override + public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) { + return config.features(Profile.Feature.KUBERNETES_SERVICE_ACCOUNTS, Profile.Feature.JWT_AUTHORIZATION_GRANT); + } + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderKubernetesTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderKubernetesTest.java index 474b02ed2c4..f7b25a7c57f 100644 --- a/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderKubernetesTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderKubernetesTest.java @@ -51,7 +51,7 @@ public class IdentityProviderKubernetesTest extends AbstractIdentityProviderTest try (Response response = managedRealm.admin().identityProviders().create(identityProvider)) { Assertions.assertEquals(400, response.getStatus()); ErrorRepresentation error = response.readEntity(ErrorRepresentation.class); - assertEquals("Issuer URL already used for IDP 'kubernetes1'", error.getErrorMessage()); + assertEquals("Issuer URL already used for IDP 'kubernetes1', Issuer must be unique if the idp supports JWT Authorization Grant or Federated Client Authentication", error.getErrorMessage()); } } @@ -84,7 +84,7 @@ public class IdentityProviderKubernetesTest extends AbstractIdentityProviderTest } catch (WebApplicationException ex) { Assertions.assertEquals(400, ex.getResponse().getStatus()); ErrorRepresentation error = ex.getResponse().readEntity(ErrorRepresentation.class); - assertEquals("Issuer URL already used for IDP 'kubernetes1'", error.getErrorMessage()); + assertEquals("Issuer URL already used for IDP 'kubernetes1', Issuer must be unique if the idp supports JWT Authorization Grant or Federated Client Authentication", error.getErrorMessage()); } } diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderOidcTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderOidcTest.java index 124ceba59b7..89073a82a02 100644 --- a/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderOidcTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderOidcTest.java @@ -494,6 +494,7 @@ public class IdentityProviderOidcTest extends AbstractIdentityProviderTest { // Successful update when JWKS URL set oidcConfig.setJwksUrl("https://foo"); + oidcConfig.setIssuer("https://foo"); resource.update(representation); managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove()); diff --git a/tests/base/src/test/java/org/keycloak/tests/client/authentication/external/FederatedClientAuthConflictsTest.java b/tests/base/src/test/java/org/keycloak/tests/client/authentication/external/FederatedClientAuthConflictsTest.java index b98fb5f3c65..41eadfad7bf 100644 --- a/tests/base/src/test/java/org/keycloak/tests/client/authentication/external/FederatedClientAuthConflictsTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/client/authentication/external/FederatedClientAuthConflictsTest.java @@ -45,8 +45,8 @@ public class FederatedClientAuthConflictsTest { @Test public void testDuplicatedIssuers() { - createIdp("idp1", "http://127.0.0.1:8500"); - createIdp("idp2", "http://127.0.0.1:8500"); + createIdp("idp1", "http://127.0.0.1:8500", false); + createIdp("idp2", "http://127.0.0.1:8500", false); createClient("myclient", "external1", "idp1"); @@ -67,7 +67,7 @@ public class FederatedClientAuthConflictsTest { Assertions.assertTrue(response.isSuccess()); Assertions.assertEquals("myclient", events.poll().getClientId()); - createIdp("idp2", "http://127.0.0.1:8500"); + IdentityProviderRepresentation idp2 = createIdp("idp2", "http://127.0.0.1:8500", false); clientRep.getAttributes().put(FederatedJWTClientAuthenticator.JWT_CREDENTIAL_ISSUER_KEY, "idp2"); realm.admin().clients().get(clientRep.getId()).update(clientRep); @@ -79,8 +79,12 @@ public class FederatedClientAuthConflictsTest { // Update old entry, so next read will invalidate it idp1.getConfig().put(IdentityProviderModel.ISSUER, "http://127.0.0.1:8501"); + idp1.getConfig().put(OIDCIdentityProviderConfig.SUPPORTS_CLIENT_ASSERTIONS, "false"); realm.admin().identityProviders().get(idp1.getAlias()).update(idp1); + idp2.getConfig().put(OIDCIdentityProviderConfig.SUPPORTS_CLIENT_ASSERTIONS, "true"); + realm.admin().identityProviders().get(idp2.getAlias()).update(idp2); + // Should succeed as entry is updated in the cache events.clear(); response = oAuthClient.clientCredentialsGrantRequest().clientJwt(createDefaultToken("external1", "http://127.0.0.1:8500")).send(); @@ -116,11 +120,15 @@ public class FederatedClientAuthConflictsTest { } private IdentityProviderRepresentation createIdp(String alias, String issuer) { + return createIdp(alias, issuer, true); + } + + private IdentityProviderRepresentation createIdp(String alias, String issuer, boolean supportsClientAssertions) { IdentityProviderRepresentation rep = IdentityProviderBuilder.create() .providerId(OIDCIdentityProviderFactory.PROVIDER_ID) .alias(alias) .setAttribute(IdentityProviderModel.ISSUER, issuer) - .setAttribute(OIDCIdentityProviderConfig.SUPPORTS_CLIENT_ASSERTIONS, "true") + .setAttribute(OIDCIdentityProviderConfig.SUPPORTS_CLIENT_ASSERTIONS, String.valueOf(supportsClientAssertions)) .setAttribute(OIDCIdentityProviderConfig.USE_JWKS_URL, "true") .setAttribute(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, "true") .setAttribute(OIDCIdentityProviderConfig.JWKS_URL, "http://127.0.0.1:8500/idp/jwks")