Details
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 c594f7f..fd49f3e 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
@@ -17,7 +17,6 @@
package org.keycloak.broker.oidc;
import org.keycloak.models.IdentityProviderModel;
-import org.keycloak.models.KeycloakSession;
/**
* @author Pedro Igor
@@ -61,6 +60,14 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
getConfig().put("publicKeySignatureVerifier", signingCertificate);
}
+ public String getPublicKeySignatureVerifierKeyId() {
+ return getConfig().get("publicKeySignatureVerifierKeyId");
+ }
+
+ public void setPublicKeySignatureVerifierKeyId(String publicKeySignatureVerifierKeyId) {
+ getConfig().put("publicKeySignatureVerifierKeyId", publicKeySignatureVerifierKeyId);
+ }
+
public boolean isValidateSignature() {
return Boolean.valueOf(getConfig().get("validateSignature"));
}
diff --git a/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java b/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java
index cf24a1b..fda2c2f 100644
--- a/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java
+++ b/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java
@@ -60,7 +60,10 @@ public class OIDCIdentityProviderPublicKeyLoader implements PublicKeyLoader {
return Collections.emptyMap();
}
- String kid = KeyUtils.createKeyId(publicKey);
+ String presetKeyId = config.getPublicKeySignatureVerifierKeyId();
+ String kid = (presetKeyId == null || presetKeyId.trim().isEmpty())
+ ? KeyUtils.createKeyId(publicKey)
+ : presetKeyId;
return Collections.singletonMap(kid, publicKey);
} catch (Exception e) {
logger.warnf(e, "Unable to retrieve publicKey for verify signature of identityProvider '%s' . Error details: %s", config.getAlias(), e.getMessage());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java
index 9bdc40e..6bc60b1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java
@@ -24,12 +24,12 @@ import javax.ws.rs.core.UriBuilder;
import org.junit.Before;
import org.junit.Test;
+
import org.keycloak.admin.client.resource.RealmResource;
-import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.common.util.*;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.keys.KeyProvider;
import org.keycloak.keys.PublicKeyStorageUtils;
-import org.keycloak.keys.loader.PublicKeyStorageManager;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
@@ -181,6 +181,38 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
assertErrorPage("Unexpected error when authenticating with identity provider");
}
+ @Test
+ public void testSignatureVerificationHardcodedPublicKeyWithKeyIdSetExplicitly() throws Exception {
+ // Configure OIDC identity provider with JWKS URL
+ IdentityProviderRepresentation idpRep = getIdentityProvider();
+ OIDCIdentityProviderConfigRep cfg = new OIDCIdentityProviderConfigRep(idpRep);
+ cfg.setValidateSignature(true);
+ cfg.setUseJwksUrl(false);
+
+ KeysMetadataRepresentation.KeyMetadataRepresentation key = ApiUtil.findActiveKey(providerRealm());
+ String pemData = key.getPublicKey();
+ cfg.setPublicKeySignatureVerifier(pemData);
+ String expectedKeyId = KeyUtils.createKeyId(PemUtils.decodePublicKey(pemData));
+ updateIdentityProvider(idpRep);
+
+ // Check that user is able to login
+ logInAsUserInIDPForFirstTime();
+ assertLoggedInAccountManagement();
+
+ logoutFromRealm(bc.consumerRealmName());
+
+ // Set key id to an invalid one
+ cfg.setPublicKeySignatureVerifierKeyId("invalid-key-id");
+ updateIdentityProvider(idpRep);
+
+ logInAsUserInIDP();
+ assertErrorPage("Unexpected error when authenticating with identity provider");
+
+ // Set key id to a valid one
+ cfg.setPublicKeySignatureVerifierKeyId(expectedKeyId);
+ updateIdentityProvider(idpRep);
+ }
+
@Test
public void testClearKeysCache() throws Exception {
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index e6a9a63..04b6d21 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -503,6 +503,8 @@ identity-provider.use-jwks-url.tooltip=If the switch is on, then identity provid
identity-provider.jwks-url.tooltip=URL where identity provider keys in JWK format are stored. See JWK specification for more details. If you use external keycloak identity provider, then you can use URL like 'http://broker-keycloak:8180/auth/realms/test/protocol/openid-connect/certs' assuming your brokered keycloak is running on 'http://broker-keycloak:8180' and it's realm is 'test' .
validating-public-key=Validating Public Key
identity-provider.validating-public-key.tooltip=The public key in PEM format that must be used to verify external IDP signatures.
+validating-public-key-id=Validating Public Key Id
+identity-provider.validating-public-key-id.tooltip=Explicit ID of the validating public key given above if the key ID. Leave unset if the external IDP is Keycloak or uses the same mechanism to determine key ID.
import-external-idp-config=Import External IDP Config
import-external-idp-config.tooltip=Allows you to load external IDP metadata from a config file or to download it from a URL.
import-from-url=Import from URL
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
index e33b52d..b4069ea 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
@@ -211,13 +211,21 @@
</div>
<div class="form-group clearfix" data-ng-hide="identityProvider.config.useJwksUrl == 'true'">
- <label class="col-md-2 control-label" for="publicKeySignatureVerifier">{{:: 'validating-public-key' | translate}}</label>
+ <label class="col-md-2 control-label" for="publicKeySignatureVerifierKey">{{:: 'validating-public-key' | translate}}</label>
<div class="col-md-6">
<textarea class="form-control" id="publicKeySignatureVerifier" ng-model="identityProvider.config.publicKeySignatureVerifier"/>
</div>
<kc-tooltip>{{:: 'identity-provider.validating-public-key.tooltip' | translate}}</kc-tooltip>
</div>
+ <div class="form-group clearfix" data-ng-hide="identityProvider.config.useJwksUrl == 'true'">
+ <label class="col-md-2 control-label" for="publicKeySignatureVerifierKeyId">{{:: 'validating-public-key-id' | translate}}</label>
+ <div class="col-md-6">
+ <input class="form-control" id="publicKeySignatureVerifierKeyId" ng-model="identityProvider.config.publicKeySignatureVerifierKeyId"/>
+ </div>
+ <kc-tooltip>{{:: 'identity-provider.validating-public-key-id.tooltip' | translate}}</kc-tooltip>
+ </div>
+
</div>
</fieldset>