KEYCLOAK-3493 KEYCLOAK-3532 Added KeyStorageProvider. Support key rotation for OIDC clients and identity providers with JWKS url.
This commit is contained in:
1 parent
6e00587468
commit
f9a0abcfc4
88 files changed
+1960
-429
No files matched your search
+2
-2
@@ -64,7 +64,7 @@ public class IdentityProviderRegistrationTest extends AbstractIdentityProviderMo
|
||||
|
||||
identityProviderModel.setAlias("custom-provider");
|
||||
|
||||
CustomSocialProvider customSocialProvider = providerFactory.create(identityProviderModel);
|
||||
CustomSocialProvider customSocialProvider = providerFactory.create(this.session, identityProviderModel);
|
||||
|
||||
assertNotNull(customSocialProvider);
|
||||
IdentityProviderModel config = customSocialProvider.getConfig();
|
||||
@@ -87,7 +87,7 @@ public class IdentityProviderRegistrationTest extends AbstractIdentityProviderMo
|
||||
|
||||
identityProviderModel.setAlias("custom-provider");
|
||||
|
||||
CustomIdentityProvider provider = providerFactory.create(identityProviderModel);
|
||||
CustomIdentityProvider provider = providerFactory.create(this.session, identityProviderModel);
|
||||
|
||||
assertNotNull(provider);
|
||||
IdentityProviderModel config = provider.getConfig();
|
||||
|
||||
+8
-8
@@ -158,7 +158,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||
}
|
||||
|
||||
private void assertGoogleIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
||||
GoogleIdentityProvider googleIdentityProvider = new GoogleIdentityProviderFactory().create(identityProvider);
|
||||
GoogleIdentityProvider googleIdentityProvider = new GoogleIdentityProviderFactory().create(session, identityProvider);
|
||||
OIDCIdentityProviderConfig config = googleIdentityProvider.getConfig();
|
||||
|
||||
assertEquals("model-google", config.getAlias());
|
||||
@@ -176,7 +176,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||
}
|
||||
|
||||
private void assertSamlIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
||||
SAMLIdentityProvider samlIdentityProvider = new SAMLIdentityProviderFactory().create(identityProvider);
|
||||
SAMLIdentityProvider samlIdentityProvider = new SAMLIdentityProviderFactory().create(session, identityProvider);
|
||||
SAMLIdentityProviderConfig config = samlIdentityProvider.getConfig();
|
||||
|
||||
assertEquals("model-saml-signed-idp", config.getAlias());
|
||||
@@ -196,7 +196,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||
}
|
||||
|
||||
private void assertOidcIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
||||
OIDCIdentityProvider googleIdentityProvider = new OIDCIdentityProviderFactory().create(identityProvider);
|
||||
OIDCIdentityProvider googleIdentityProvider = new OIDCIdentityProviderFactory().create(session, identityProvider);
|
||||
OIDCIdentityProviderConfig config = googleIdentityProvider.getConfig();
|
||||
|
||||
assertEquals("model-oidc-idp", config.getAlias());
|
||||
@@ -210,7 +210,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||
}
|
||||
|
||||
private void assertFacebookIdentityProviderConfig(RealmModel realm, IdentityProviderModel identityProvider) {
|
||||
FacebookIdentityProvider facebookIdentityProvider = new FacebookIdentityProviderFactory().create(identityProvider);
|
||||
FacebookIdentityProvider facebookIdentityProvider = new FacebookIdentityProviderFactory().create(session, identityProvider);
|
||||
OAuth2IdentityProviderConfig config = facebookIdentityProvider.getConfig();
|
||||
|
||||
assertEquals("model-facebook", config.getAlias());
|
||||
@@ -229,7 +229,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||
}
|
||||
|
||||
private void assertGitHubIdentityProviderConfig(RealmModel realm, IdentityProviderModel identityProvider) {
|
||||
GitHubIdentityProvider gitHubIdentityProvider = new GitHubIdentityProviderFactory().create(identityProvider);
|
||||
GitHubIdentityProvider gitHubIdentityProvider = new GitHubIdentityProviderFactory().create(session, identityProvider);
|
||||
OAuth2IdentityProviderConfig config = gitHubIdentityProvider.getConfig();
|
||||
|
||||
assertEquals("model-github", config.getAlias());
|
||||
@@ -248,7 +248,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||
}
|
||||
|
||||
private void assertLinkedInIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
||||
LinkedInIdentityProvider liIdentityProvider = new LinkedInIdentityProviderFactory().create(identityProvider);
|
||||
LinkedInIdentityProvider liIdentityProvider = new LinkedInIdentityProviderFactory().create(session, identityProvider);
|
||||
OAuth2IdentityProviderConfig config = liIdentityProvider.getConfig();
|
||||
|
||||
assertEquals("model-linkedin", config.getAlias());
|
||||
@@ -265,7 +265,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||
}
|
||||
|
||||
private void assertStackoverflowIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
||||
StackoverflowIdentityProvider soIdentityProvider = new StackoverflowIdentityProviderFactory().create(identityProvider);
|
||||
StackoverflowIdentityProvider soIdentityProvider = new StackoverflowIdentityProviderFactory().create(session, identityProvider);
|
||||
StackOverflowIdentityProviderConfig config = soIdentityProvider.getConfig();
|
||||
|
||||
assertEquals("model-stackoverflow", config.getAlias());
|
||||
@@ -283,7 +283,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||
}
|
||||
|
||||
private void assertTwitterIdentityProviderConfig(IdentityProviderModel identityProvider) {
|
||||
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(identityProvider);
|
||||
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(session, identityProvider);
|
||||
OAuth2IdentityProviderConfig config = twitterIdentityProvider.getConfig();
|
||||
|
||||
assertEquals("model-twitter", config.getAlias());
|
||||
|
||||
+1
@@ -180,4 +180,5 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityP
|
||||
public void testAccountManagementLinkedIdentityAlreadyExists() {
|
||||
super.testAccountManagementLinkedIdentityAlreadyExists();
|
||||
}
|
||||
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.testsuite.broker;
|
||||
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.Constants;
|
||||
import org.keycloak.testsuite.KeycloakServer;
|
||||
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class OIDCKeycloakServerBrokerWithSignatureTest extends AbstractIdentityProviderTest {
|
||||
|
||||
private static final int PORT = 8082;
|
||||
|
||||
private static Keycloak keycloak1;
|
||||
private static Keycloak keycloak2;
|
||||
|
||||
@ClassRule
|
||||
public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
|
||||
|
||||
@Override
|
||||
protected void configureServer(KeycloakServer server) {
|
||||
server.getConfig().setPort(PORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
|
||||
server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-kc-oidc.json"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getTestRealms() {
|
||||
return new String[] { "realm-with-oidc-identity-provider" };
|
||||
}
|
||||
};
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClazz() {
|
||||
keycloak1 = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID);
|
||||
keycloak2 = Keycloak.getInstance("http://localhost:8082/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBefore() {
|
||||
super.onBefore();
|
||||
|
||||
// Enable validate signatures
|
||||
IdentityProviderModel idpModel = getIdentityProviderModel();
|
||||
OIDCIdentityProviderConfig cfg = new OIDCIdentityProviderConfig(idpModel);
|
||||
cfg.setValidateSignature(true);
|
||||
getRealm().updateIdentityProvider(cfg);
|
||||
|
||||
brokerServerRule.stopSession(this.session, true);
|
||||
this.session = brokerServerRule.startSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProviderId() {
|
||||
return "kc-oidc-idp";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignatureVerificationJwksUrl() throws Exception {
|
||||
// Configure OIDC identity provider with JWKS URL
|
||||
IdentityProviderModel idpModel = getIdentityProviderModel();
|
||||
OIDCIdentityProviderConfig cfg = new OIDCIdentityProviderConfig(idpModel);
|
||||
cfg.setUseJwksUrl(true);
|
||||
|
||||
UriBuilder b = OIDCLoginProtocolService.certsUrl(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT).port(PORT));
|
||||
String jwksUrl = b.build("realm-with-oidc-identity-provider").toString();
|
||||
cfg.setJwksUrl(jwksUrl);
|
||||
getRealm().updateIdentityProvider(cfg);
|
||||
|
||||
brokerServerRule.stopSession(this.session, true);
|
||||
this.session = brokerServerRule.startSession();
|
||||
|
||||
// Check that user is able to login
|
||||
assertSuccessfulAuthentication(getIdentityProviderModel(), "test-user", "test-user@localhost", false);
|
||||
|
||||
// Rotate public keys on the parent broker
|
||||
RealmRepresentation realm = keycloak2.realm("realm-with-oidc-identity-provider").toRepresentation();
|
||||
realm.setPublicKey(org.keycloak.models.Constants.GENERATE);
|
||||
keycloak2.realm("realm-with-oidc-identity-provider").update(realm);
|
||||
|
||||
// User not able to login now as new keys can't be yet downloaded (10s timeout)
|
||||
loginIDP("test-user");
|
||||
assertTrue(errorPage.isCurrent());
|
||||
assertEquals("Unexpected error when authenticating with identity provider", errorPage.getError());
|
||||
|
||||
keycloak2.realm("realm-with-oidc-identity-provider").logoutAll();
|
||||
|
||||
// Set time offset. New keys can be downloaded. Check that user is able to login.
|
||||
Time.setOffset(20);
|
||||
|
||||
assertSuccessfulAuthentication(getIdentityProviderModel(), "test-user", "test-user@localhost", false);
|
||||
|
||||
Time.setOffset(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignatureVerificationHardcodedPublicKey() throws Exception {
|
||||
// Configure OIDC identity provider with publicKeySignatureVerifier
|
||||
IdentityProviderModel idpModel = getIdentityProviderModel();
|
||||
OIDCIdentityProviderConfig cfg = new OIDCIdentityProviderConfig(idpModel);
|
||||
cfg.setUseJwksUrl(false);
|
||||
|
||||
RealmRepresentation realm = keycloak2.realm("realm-with-oidc-identity-provider").toRepresentation();
|
||||
cfg.setPublicKeySignatureVerifier(realm.getPublicKey());
|
||||
getRealm().updateIdentityProvider(cfg);
|
||||
|
||||
brokerServerRule.stopSession(this.session, true);
|
||||
this.session = brokerServerRule.startSession();
|
||||
|
||||
// Check that user is able to login
|
||||
assertSuccessfulAuthentication(getIdentityProviderModel(), "test-user", "test-user@localhost", false);
|
||||
|
||||
// Rotate public keys on the parent broker
|
||||
realm.setPublicKey(org.keycloak.models.Constants.GENERATE);
|
||||
keycloak2.realm("realm-with-oidc-identity-provider").update(realm);
|
||||
|
||||
// User not able to login now as new keys can't be yet downloaded (10s timeout)
|
||||
loginIDP("test-user");
|
||||
assertTrue(errorPage.isCurrent());
|
||||
assertEquals("Unexpected error when authenticating with identity provider", errorPage.getError());
|
||||
|
||||
keycloak2.realm("realm-with-oidc-identity-provider").logoutAll();
|
||||
|
||||
// Even after time offset is user not able to login, because it uses old key hardcoded in identityProvider config
|
||||
Time.setOffset(20);
|
||||
|
||||
loginIDP("test-user");
|
||||
assertTrue(errorPage.isCurrent());
|
||||
assertEquals("Unexpected error when authenticating with identity provider", errorPage.getError());
|
||||
|
||||
keycloak2.realm("realm-with-oidc-identity-provider").logoutAll();
|
||||
|
||||
Time.setOffset(0);
|
||||
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -29,8 +29,8 @@ import javax.ws.rs.core.Response;
|
||||
*/
|
||||
public class CustomIdentityProvider extends AbstractIdentityProvider<IdentityProviderModel> {
|
||||
|
||||
public CustomIdentityProvider(IdentityProviderModel config) {
|
||||
super(config);
|
||||
public CustomIdentityProvider(KeycloakSession session, IdentityProviderModel config) {
|
||||
super(session, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-2
@@ -18,6 +18,7 @@ package org.keycloak.testsuite.broker.provider;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
@@ -32,8 +33,8 @@ public class CustomIdentityProviderFactory extends AbstractIdentityProviderFacto
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomIdentityProvider create(IdentityProviderModel model) {
|
||||
return new CustomIdentityProvider(model);
|
||||
public CustomIdentityProvider create(KeycloakSession session, IdentityProviderModel model) {
|
||||
return new CustomIdentityProvider(session, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ import javax.ws.rs.core.Response;
|
||||
*/
|
||||
public class CustomSocialProvider extends AbstractIdentityProvider<IdentityProviderModel> implements SocialIdentityProvider<IdentityProviderModel> {
|
||||
|
||||
public CustomSocialProvider(IdentityProviderModel config) {
|
||||
super(config);
|
||||
public CustomSocialProvider(KeycloakSession session, IdentityProviderModel config) {
|
||||
super(session, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-2
@@ -19,6 +19,7 @@ package org.keycloak.testsuite.broker.provider.social;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
@@ -33,8 +34,8 @@ public class CustomSocialProviderFactory extends AbstractIdentityProviderFactory
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomSocialProvider create(IdentityProviderModel model) {
|
||||
return new CustomSocialProvider(model);
|
||||
public CustomSocialProvider create(KeycloakSession session, IdentityProviderModel model) {
|
||||
return new CustomSocialProvider(session, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in new issue
Block a user