KEYCLOAK-4167 Make OIDC identity provider key ID configurable

This commit is contained in:
Hynek Mlnarik 2017-01-11 18:24:22 +01:00
parent 23c8809598
commit e11957ecf3
5 changed files with 57 additions and 5 deletions

View file

@ -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"));
}

View file

@ -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());

View file

@ -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 {

View file

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

View file

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