Merge pull request #3769 from hmlnarik/KEYCLOAK-4167-Unable-to-validate-access-token-for-OIDC-External-IDP-using-configured-public-key
KEYCLOAK-4167 Always use preset key for verification if key ID not set
This commit is contained in:
commit
15d0a116ac
4 changed files with 94 additions and 5 deletions
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2017 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.keys.loader;
|
||||
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.keys.PublicKeyLoader;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class HardcodedPublicKeyLoader implements PublicKeyLoader {
|
||||
|
||||
private final String kid;
|
||||
private final String pem;
|
||||
|
||||
public HardcodedPublicKeyLoader(String kid, String pem) {
|
||||
this.kid = kid;
|
||||
this.pem = pem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, PublicKey> loadKeys() throws Exception {
|
||||
return Collections.unmodifiableMap(Collections.singletonMap(kid, getSavedPublicKey()));
|
||||
}
|
||||
|
||||
protected PublicKey getSavedPublicKey() {
|
||||
if (pem != null && ! pem.trim().equals("")) {
|
||||
return PemUtils.decodePublicKey(pem);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,17 +21,20 @@ import java.security.PublicKey;
|
|||
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.keys.PublicKeyStorageProvider;
|
||||
import org.keycloak.keys.PublicKeyStorageUtils;
|
||||
import org.keycloak.keys.*;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class PublicKeyStorageManager {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PublicKeyStorageManager.class);
|
||||
|
||||
public static PublicKey getClientPublicKey(KeycloakSession session, ClientModel client, JWSInput input) {
|
||||
String kid = input.getHeader().getKeyId();
|
||||
|
||||
|
@ -44,13 +47,31 @@ public class PublicKeyStorageManager {
|
|||
|
||||
|
||||
public static PublicKey getIdentityProviderPublicKey(KeycloakSession session, RealmModel realm, OIDCIdentityProviderConfig idpConfig, JWSInput input) {
|
||||
boolean keyIdSetInConfiguration = idpConfig.getPublicKeySignatureVerifierKeyId() != null
|
||||
&& ! idpConfig.getPublicKeySignatureVerifierKeyId().trim().isEmpty();
|
||||
|
||||
String kid = input.getHeader().getKeyId();
|
||||
|
||||
PublicKeyStorageProvider keyStorage = session.getProvider(PublicKeyStorageProvider.class);
|
||||
|
||||
String modelKey = PublicKeyStorageUtils.getIdpModelCacheKey(realm.getId(), idpConfig.getInternalId());
|
||||
OIDCIdentityProviderPublicKeyLoader loader = new OIDCIdentityProviderPublicKeyLoader(session, idpConfig);
|
||||
return keyStorage.getPublicKey(modelKey, kid, loader);
|
||||
PublicKeyLoader loader;
|
||||
if (idpConfig.isUseJwksUrl()) {
|
||||
loader = new OIDCIdentityProviderPublicKeyLoader(session, idpConfig);
|
||||
} else {
|
||||
String pem = idpConfig.getPublicKeySignatureVerifier();
|
||||
|
||||
if (pem == null || pem.trim().isEmpty()) {
|
||||
logger.warnf("No public key saved on identityProvider %s", idpConfig.getAlias());
|
||||
return null;
|
||||
}
|
||||
|
||||
loader = new HardcodedPublicKeyLoader(
|
||||
keyIdSetInConfiguration
|
||||
? idpConfig.getPublicKeySignatureVerifierKeyId().trim()
|
||||
: kid, pem);
|
||||
}
|
||||
|
||||
return keyStorage.getPublicKey(modelKey, kid, loader);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,6 +211,23 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
|
|||
// Set key id to a valid one
|
||||
cfg.setPublicKeySignatureVerifierKeyId(expectedKeyId);
|
||||
updateIdentityProvider(idpRep);
|
||||
logInAsUserInIDP();
|
||||
assertLoggedInAccountManagement();
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
|
||||
// Set key id to empty
|
||||
cfg.setPublicKeySignatureVerifierKeyId("");
|
||||
updateIdentityProvider(idpRep);
|
||||
logInAsUserInIDP();
|
||||
assertLoggedInAccountManagement();
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
|
||||
// Unset key id
|
||||
cfg.setPublicKeySignatureVerifierKeyId(null);
|
||||
updateIdentityProvider(idpRep);
|
||||
logInAsUserInIDP();
|
||||
assertLoggedInAccountManagement();
|
||||
logoutFromRealm(bc.consumerRealmName());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -504,7 +504,7 @@ identity-provider.jwks-url.tooltip=URL where identity provider keys in JWK forma
|
|||
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.
|
||||
identity-provider.validating-public-key-id.tooltip=Explicit ID of the validating public key given above if the key ID. Leave blank if the key above should be used always, regardless of key ID specified by external IDP; set it if the key should only be used for verifying if key ID from external IDP matches.
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue