diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
index d7985b0714..925a0dba15 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
@@ -17,26 +17,21 @@
package org.keycloak.adapters;
-import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProvider;
+import org.keycloak.adapters.authorization.PolicyEnforcer;
+import org.keycloak.adapters.rotation.PublicKeyLocator;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.enums.RelativeUrlsUsed;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.enums.TokenStore;
import org.keycloak.representations.adapters.config.AdapterConfig;
-import org.keycloak.representations.idm.PublishedRealmRepresentation;
-import org.keycloak.util.JsonSerialization;
import org.keycloak.common.util.KeycloakUriBuilder;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.net.URI;
-import java.security.PublicKey;
import java.util.Map;
/**
@@ -57,7 +52,7 @@ public class AdapterDeploymentContext {
* during the application deployment's life cycle.
*
* @param deployment A KeycloakConfigResolver, possibly missing the Auth
- * Server URL and/or Realm Public Key
+ * Server URL
*/
public AdapterDeploymentContext(KeycloakDeployment deployment) {
this.deployment = deployment;
@@ -79,7 +74,6 @@ public class AdapterDeploymentContext {
/**
* For single-tenant deployments, it complements KeycloakDeployment
* by resolving a relative Auth Server's URL based on the current request
- * and, if needed, will lazily resolve the Realm's Public Key.
*
* For multi-tenant deployments, defers the resolution of KeycloakDeployment
* to the KeycloakConfigResolver .
@@ -98,8 +92,8 @@ public class AdapterDeploymentContext {
if (deployment.getAuthServerBaseUrl() == null) return deployment;
KeycloakDeployment resolvedDeployment = resolveUrls(deployment, facade);
- if (resolvedDeployment.getRealmKey() == null) {
- resolveRealmKey(resolvedDeployment);
+ if (resolvedDeployment.getPublicKeyLocator() == null) {
+ throw new RuntimeException("KeycloakDeployment was never initialized through appropriate SPIs");
}
return resolvedDeployment;
}
@@ -115,45 +109,6 @@ public class AdapterDeploymentContext {
}
}
- public void resolveRealmKey(KeycloakDeployment deployment) {
- if (deployment.getClient() == null) {
- throw new RuntimeException("KeycloakDeployment was never initialized through appropriate SPIs");
- }
- HttpGet get = new HttpGet(deployment.getRealmInfoUrl());
- try {
- HttpResponse response = deployment.getClient().execute(get);
- int status = response.getStatusLine().getStatusCode();
- if (status != 200) {
- close(response);
- throw new RuntimeException("Unable to resolve realm public key remotely, status = " + status);
- }
- HttpEntity entity = response.getEntity();
- if (entity == null) {
- throw new RuntimeException("Unable to resolve realm public key remotely. There was no entity.");
- }
- InputStream is = entity.getContent();
- try {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- int c;
- while ((c = is.read()) != -1) {
- os.write(c);
- }
- byte[] bytes = os.toByteArray();
- String json = new String(bytes);
- PublishedRealmRepresentation rep = JsonSerialization.readValue(json, PublishedRealmRepresentation.class);
- deployment.setRealmKey(rep.getPublicKey());
- } finally {
- try {
- is.close();
- } catch (IOException ignored) {
-
- }
- }
- } catch (IOException e) {
- throw new RuntimeException("Unable to resolve realm public key remotely", e);
- }
- }
-
/**
* This delegate is used to store temporary, per-request metadata like request resolved URLs.
* Ever method is delegated except URL get methods and isConfigured()
@@ -207,6 +162,11 @@ public class AdapterDeploymentContext {
return (this.unregisterNodeUrl != null) ? this.unregisterNodeUrl : delegate.getUnregisterNodeUrl();
}
+ @Override
+ public String getJwksUrl() {
+ return (this.jwksUrl != null) ? this.jwksUrl : delegate.getJwksUrl();
+ }
+
@Override
public String getResourceName() {
return delegate.getResourceName();
@@ -223,13 +183,13 @@ public class AdapterDeploymentContext {
}
@Override
- public PublicKey getRealmKey() {
- return delegate.getRealmKey();
+ public void setPublicKeyLocator(PublicKeyLocator publicKeyLocator) {
+ delegate.setPublicKeyLocator(publicKeyLocator);
}
@Override
- public void setRealmKey(PublicKey realmKey) {
- delegate.setRealmKey(realmKey);
+ public PublicKeyLocator getPublicKeyLocator() {
+ return delegate.getPublicKeyLocator();
}
@Override
@@ -466,6 +426,26 @@ public class AdapterDeploymentContext {
public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) {
delegate.setTokenMinimumTimeToLive(tokenMinimumTimeToLive);
}
+
+ @Override
+ public PolicyEnforcer getPolicyEnforcer() {
+ return delegate.getPolicyEnforcer();
+ }
+
+ @Override
+ public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) {
+ delegate.setPolicyEnforcer(policyEnforcer);
+ }
+
+ @Override
+ public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) {
+ delegate.setMinTimeBetweenJwksRequests(minTimeBetweenJwksRequests);
+ }
+
+ @Override
+ public int getMinTimeBetweenJwksRequests() {
+ return delegate.getMinTimeBetweenJwksRequests();
+ }
}
protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
index 9af6214ba2..70b90b1044 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
@@ -19,6 +19,7 @@ package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
+import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
@@ -84,7 +85,7 @@ public class BearerTokenRequestAuthenticator {
protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
try {
- token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
+ token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
} catch (VerificationException e) {
log.error("Failed to verify token", e);
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", e.getMessage());
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java
index 7f66dbf2e6..2093645533 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java
@@ -22,6 +22,7 @@ import java.io.IOException;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.RSATokenVerifier;
+import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.VerificationException;
import org.keycloak.constants.AdapterConstants;
@@ -73,7 +74,7 @@ public class CookieTokenStore {
try {
// Skip check if token is active now. It's supposed to be done later by the caller
- AccessToken accessToken = RSATokenVerifier.verifyToken(accessTokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl(), false, true);
+ AccessToken accessToken = AdapterRSATokenVerifier.verifyToken(accessTokenString, deployment, false, true);
IDToken idToken;
if (idTokenString != null && idTokenString.length() > 0) {
try {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpAdapterUtils.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpAdapterUtils.java
new file mode 100644
index 0000000000..e01f7dc32e
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpAdapterUtils.java
@@ -0,0 +1,79 @@
+/*
+ * 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.adapters;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author Marek Posolda
+ */
+public class HttpAdapterUtils {
+
+
+ public static T sendJsonHttpRequest(KeycloakDeployment deployment, HttpRequestBase httpRequest, Class clazz) throws HttpClientAdapterException {
+ try {
+ HttpResponse response = deployment.getClient().execute(httpRequest);
+ int status = response.getStatusLine().getStatusCode();
+ if (status != 200) {
+ close(response);
+ throw new HttpClientAdapterException("Unexpected status = " + status);
+ }
+ HttpEntity entity = response.getEntity();
+ if (entity == null) {
+ throw new HttpClientAdapterException("There was no entity.");
+ }
+ InputStream is = entity.getContent();
+ try {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ int c;
+ while ((c = is.read()) != -1) {
+ os.write(c);
+ }
+ byte[] bytes = os.toByteArray();
+ String json = new String(bytes);
+ return JsonSerialization.readValue(json, clazz);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException ignored) {
+
+ }
+ }
+ } catch (IOException e) {
+ throw new HttpClientAdapterException("IO error", e);
+ }
+ }
+
+
+ private static void close(HttpResponse response) {
+ if (response.getEntity() != null) {
+ try {
+ response.getEntity().getContent().close();
+ } catch (IOException e) {
+
+ }
+ }
+ }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientAdapterException.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientAdapterException.java
new file mode 100644
index 0000000000..7d303e290d
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientAdapterException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.adapters;
+
+/**
+ * @author Marek Posolda
+ */
+public class HttpClientAdapterException extends Exception {
+
+ public HttpClientAdapterException(String message) {
+ super(message);
+ }
+
+ public HttpClientAdapterException(String message, Throwable t) {
+ super(message, t);
+ }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
index 901b3ea99c..30c40c37ca 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
@@ -21,6 +21,7 @@ import org.apache.http.client.HttpClient;
import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProvider;
import org.keycloak.adapters.authorization.PolicyEnforcer;
+import org.keycloak.adapters.rotation.PublicKeyLocator;
import org.keycloak.constants.ServiceUrlConstants;
import org.keycloak.common.enums.RelativeUrlsUsed;
import org.keycloak.common.enums.SslRequired;
@@ -29,7 +30,6 @@ import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.common.util.KeycloakUriBuilder;
import java.net.URI;
-import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
@@ -43,7 +43,7 @@ public class KeycloakDeployment {
protected RelativeUrlsUsed relativeUrls;
protected String realm;
- protected volatile PublicKey realmKey;
+ protected PublicKeyLocator publicKeyLocator;
protected String authServerBaseUrl;
protected String realmInfoUrl;
protected KeycloakUriBuilder authUrl;
@@ -52,6 +52,7 @@ public class KeycloakDeployment {
protected String accountUrl;
protected String registerNodeUrl;
protected String unregisterNodeUrl;
+ protected String jwksUrl;
protected String principalAttribute = "sub";
protected String resourceName;
@@ -79,13 +80,14 @@ public class KeycloakDeployment {
protected volatile int notBefore;
protected int tokenMinimumTimeToLive;
+ protected int minTimeBetweenJwksRequests;
private PolicyEnforcer policyEnforcer;
public KeycloakDeployment() {
}
public boolean isConfigured() {
- return getRealm() != null && getRealmKey() != null && (isBearerOnly() || getAuthServerBaseUrl() != null);
+ return getRealm() != null && getPublicKeyLocator() != null && (isBearerOnly() || getAuthServerBaseUrl() != null);
}
public String getResourceName() {
@@ -100,12 +102,12 @@ public class KeycloakDeployment {
this.realm = realm;
}
- public PublicKey getRealmKey() {
- return realmKey;
+ public PublicKeyLocator getPublicKeyLocator() {
+ return publicKeyLocator;
}
- public void setRealmKey(PublicKey realmKey) {
- this.realmKey = realmKey;
+ public void setPublicKeyLocator(PublicKeyLocator publicKeyLocator) {
+ this.publicKeyLocator = publicKeyLocator;
}
public String getAuthServerBaseUrl() {
@@ -147,6 +149,7 @@ public class KeycloakDeployment {
accountUrl = authUrlBuilder.clone().path(ServiceUrlConstants.ACCOUNT_SERVICE_PATH).build(getRealm()).toString();
registerNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_REGISTER_NODE_PATH).build(getRealm()).toString();
unregisterNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH).build(getRealm()).toString();
+ jwksUrl = authUrlBuilder.clone().path(ServiceUrlConstants.JWKS_URL).build(getRealm()).toString();
}
public RelativeUrlsUsed getRelativeUrls() {
@@ -181,6 +184,10 @@ public class KeycloakDeployment {
return unregisterNodeUrl;
}
+ public String getJwksUrl() {
+ return jwksUrl;
+ }
+
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
@@ -369,6 +376,14 @@ public class KeycloakDeployment {
this.tokenMinimumTimeToLive = tokenMinimumTimeToLive;
}
+ public int getMinTimeBetweenJwksRequests() {
+ return minTimeBetweenJwksRequests;
+ }
+
+ public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) {
+ this.minTimeBetweenJwksRequests = minTimeBetweenJwksRequests;
+ }
+
public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) {
this.policyEnforcer = policyEnforcer;
}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
index 5d54df9bab..04e16f895a 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
@@ -22,6 +22,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.adapters.authorization.PolicyEnforcer;
+import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator;
+import org.keycloak.adapters.rotation.JWKPublicKeyLocator;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.PemUtils;
import org.keycloak.enums.TokenStore;
@@ -59,11 +61,16 @@ public class KeycloakDeploymentBuilder {
PublicKey realmKey;
try {
realmKey = PemUtils.decodePublicKey(realmKeyPem);
+ HardcodedPublicKeyLocator pkLocator = new HardcodedPublicKeyLocator(realmKey);
+ deployment.setPublicKeyLocator(pkLocator);
} catch (Exception e) {
throw new RuntimeException(e);
}
- deployment.setRealmKey(realmKey);
+ } else {
+ JWKPublicKeyLocator pkLocator = new JWKPublicKeyLocator();
+ deployment.setPublicKeyLocator(pkLocator);
}
+
if (adapterConfig.getSslRequired() != null) {
deployment.setSslRequired(SslRequired.valueOf(adapterConfig.getSslRequired().toUpperCase()));
} else {
@@ -97,6 +104,7 @@ public class KeycloakDeploymentBuilder {
deployment.setRegisterNodeAtStartup(adapterConfig.isRegisterNodeAtStartup());
deployment.setRegisterNodePeriod(adapterConfig.getRegisterNodePeriod());
deployment.setTokenMinimumTimeToLive(adapterConfig.getTokenMinimumTimeToLive());
+ deployment.setMinTimeBetweenJwksRequests(adapterConfig.getMinTimeBetweenJwksRequests());
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
index 73aa0f52e7..02637c0810 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
@@ -20,6 +20,7 @@ package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.RSATokenVerifier;
+import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
@@ -342,7 +343,7 @@ public class OAuthRequestAuthenticator {
refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken();
try {
- token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
+ token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
if (idTokenString != null) {
try {
JWSInput input = new JWSInput(idTokenString);
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java
index 1a8c735afd..5a1df8c1b6 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java
@@ -17,7 +17,10 @@
package org.keycloak.adapters;
+import java.security.PublicKey;
+
import org.jboss.logging.Logger;
+import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.UserSessionManagement;
import org.keycloak.jose.jws.JWSInputException;
@@ -198,7 +201,8 @@ public class PreAuthActionsHandler {
try {
JWSInput input = new JWSInput(token);
- if (RSAProvider.verify(input, deployment.getRealmKey())) {
+ PublicKey publicKey = AdapterRSATokenVerifier.getPublicKey(input, deployment);
+ if (RSAProvider.verify(input, publicKey)) {
return input;
}
} catch (JWSInputException ignore) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
index 7cfc7a698b..75f0cb8f2a 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
@@ -21,6 +21,7 @@ import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.RSATokenVerifier;
+import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessToken;
@@ -130,7 +131,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
String tokenString = response.getToken();
AccessToken token = null;
try {
- token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
+ token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
log.debug("Token Verification succeeded!");
} catch (VerificationException e) {
log.error("failed verification of token");
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
index f1be944353..1c900b8657 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
@@ -21,6 +21,7 @@ import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.AuthorizationDeniedException;
import org.keycloak.authorization.client.AuthzClient;
@@ -120,7 +121,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
AuthorizationResponse authzResponse = authzClient.authorization(accessToken).authorize(authzRequest);
if (authzResponse != null) {
- return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
+ return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
}
return null;
@@ -130,7 +131,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
if (token.getAuthorization() == null) {
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getClientId());
- return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
+ return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
} else {
EntitlementRequest request = new EntitlementRequest();
PermissionRequest permissionRequest = new PermissionRequest();
@@ -139,7 +140,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
permissionRequest.setScopes(new HashSet<>(pathConfig.getScopes()));
request.addPermission(permissionRequest);
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).get(authzClient.getConfiguration().getClientId(), request);
- return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
+ return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
}
}
} catch (AuthorizationDeniedException e) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java
index 51be55137f..75086e7804 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java
@@ -43,6 +43,7 @@ import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.FindFile;
import org.keycloak.representations.AccessToken;
@@ -88,9 +89,6 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
try {
InputStream is = FindFile.findFile(keycloakConfigFile);
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
- if (kd.getRealmKey() == null) {
- new AdapterDeploymentContext().resolveRealmKey(kd);
- }
return kd;
} catch (RuntimeException e) {
getLogger().debug("Unable to find or parse file " + keycloakConfigFile + " due to " + e.getMessage(), e);
@@ -190,7 +188,7 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
protected Auth bearerAuth(String tokenString) throws VerificationException {
- AccessToken token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
+ AccessToken token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
boolean verifyCaller;
if (deployment.isUseResourceRoleMappings()) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/AdapterRSATokenVerifier.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/AdapterRSATokenVerifier.java
new file mode 100644
index 0000000000..c69ee38d65
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/AdapterRSATokenVerifier.java
@@ -0,0 +1,64 @@
+/*
+ * 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.adapters.rotation;
+
+import java.security.PublicKey;
+
+import org.jboss.logging.Logger;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.common.VerificationException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.representations.AccessToken;
+
+/**
+ * @author Marek Posolda
+ */
+public class AdapterRSATokenVerifier {
+
+ private static final Logger log = Logger.getLogger(AdapterRSATokenVerifier.class);
+
+ public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment) throws VerificationException {
+ return verifyToken(tokenString, deployment, true, true);
+ }
+
+
+ public static PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) throws VerificationException {
+ PublicKeyLocator pkLocator = deployment.getPublicKeyLocator();
+
+ PublicKey publicKey = pkLocator.getPublicKey(input, deployment);
+ if (publicKey == null) {
+ log.errorf("Didn't find publicKey for kid: %s", input.getHeader().getKeyId());
+ throw new VerificationException("Didn't find publicKey for specified kid");
+ }
+
+ return publicKey;
+ }
+
+ public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment, boolean checkActive, boolean checkTokenType) throws VerificationException {
+ JWSInput input;
+ try {
+ input = new JWSInput(tokenString);
+ } catch (Exception e) {
+ throw new VerificationException("Couldn't parse token", e);
+ }
+
+ PublicKey publicKey = getPublicKey(input, deployment);
+ return RSATokenVerifier.verifyToken(input, publicKey, deployment.getRealmInfoUrl(), checkActive, checkTokenType);
+ }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java
new file mode 100644
index 0000000000..40fb71ad77
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java
@@ -0,0 +1,40 @@
+/*
+ * 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.adapters.rotation;
+
+import java.security.PublicKey;
+
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.jose.jws.JWSInput;
+
+/**
+ * @author Marek Posolda
+ */
+public class HardcodedPublicKeyLocator implements PublicKeyLocator {
+
+ private PublicKey publicKey;
+
+ public HardcodedPublicKeyLocator(PublicKey publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ @Override
+ public PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) {
+ return publicKey;
+ }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java
new file mode 100644
index 0000000000..500392338c
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java
@@ -0,0 +1,107 @@
+/*
+ * 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.adapters.rotation;
+
+import java.security.PublicKey;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.http.client.methods.HttpGet;
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.HttpAdapterUtils;
+import org.keycloak.adapters.HttpClientAdapterException;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.common.util.Time;
+import org.keycloak.jose.jwk.JSONWebKeySet;
+import org.keycloak.jose.jwk.JWK;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.util.JWKSUtils;
+
+/**
+ * When needed, publicKeys are downloaded by sending request to realm's jwks_url
+ *
+ * @author Marek Posolda
+ */
+public class JWKPublicKeyLocator implements PublicKeyLocator {
+
+ private static final Logger log = Logger.getLogger(JWKPublicKeyLocator.class);
+
+ private Map currentKeys = new ConcurrentHashMap<>();
+
+ private volatile int lastRequestTime = 0;
+
+ @Override
+ public PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) {
+ String kid = input.getHeader().getKeyId();
+ return getPublicKey(kid, deployment);
+ }
+
+
+ private PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
+ int minTimeBetweenRequests = deployment.getMinTimeBetweenJwksRequests();
+
+ // Check if key is in cache.
+ PublicKey publicKey = currentKeys.get(kid);
+ if (publicKey != null) {
+ return publicKey;
+ }
+
+ int currentTime = Time.currentTime();
+
+ // Check if we are allowed to send request
+ if (currentTime > lastRequestTime + minTimeBetweenRequests) {
+ synchronized (this) {
+ currentTime = Time.currentTime();
+ if (currentTime > lastRequestTime + minTimeBetweenRequests) {
+ sendRequest(deployment);
+ lastRequestTime = currentTime;
+ } else {
+ // TODO: debug
+ log.infof("Won't send request to realm jwks url. Last request time was %d", lastRequestTime);
+ }
+ }
+ }
+
+ return currentKeys.get(kid);
+
+ }
+
+
+ private void sendRequest(KeycloakDeployment deployment) {
+ // Send the request
+ // TODO: trace or remove?
+ log.infof("Going to send request to retrieve new set of realm public keys for client %s", deployment.getResourceName());
+
+ HttpGet getMethod = new HttpGet(deployment.getJwksUrl());
+ try {
+ JSONWebKeySet jwks = HttpAdapterUtils.sendJsonHttpRequest(deployment, getMethod, JSONWebKeySet.class);
+
+ Map publicKeys = JWKSUtils.getKeysForUse(jwks, JWK.Use.SIG);
+
+ // TODO: Debug with condition
+ log.infof("Realm public keys successfully retrieved for client %s. New kids: %s", deployment.getResourceName(), publicKeys.keySet().toString());
+
+ // Update current keys
+ currentKeys.clear();
+ currentKeys.putAll(publicKeys);
+
+ } catch (HttpClientAdapterException e) {
+ log.error("Error when sending request to retrieve realm keys", e);
+ }
+ }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java
new file mode 100644
index 0000000000..bda80dc3a3
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.adapters.rotation;
+
+import java.security.PublicKey;
+
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.jose.jws.JWSInput;
+
+/**
+ * @author Marek Posolda
+ */
+public interface PublicKeyLocator {
+
+ /**
+ * @param input
+ * @param deployment
+ * @return publicKey, which should be used for verify signature on given "input"
+ */
+ PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment);
+
+}
diff --git a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
index 4be511023c..d1ce748f63 100644
--- a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
+++ b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
@@ -21,6 +21,8 @@ import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.junit.Test;
import org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider;
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
+import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator;
+import org.keycloak.adapters.rotation.JWKPublicKeyLocator;
import org.keycloak.common.enums.RelativeUrlsUsed;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.enums.TokenStore;
@@ -39,7 +41,11 @@ public class KeycloakDeploymentBuilderTest {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak.json"));
assertEquals("demo", deployment.getRealm());
assertEquals("customer-portal", deployment.getResourceName());
- assertEquals(PemUtils.decodePublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"), deployment.getRealmKey());
+
+ assertTrue(deployment.getPublicKeyLocator() instanceof HardcodedPublicKeyLocator);
+ assertEquals(PemUtils.decodePublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"),
+ deployment.getPublicKeyLocator().getPublicKey(null, deployment));
+
assertEquals("https://localhost:8443/auth/realms/demo/protocol/openid-connect/auth", deployment.getAuthUrl().build().toString());
assertEquals(SslRequired.EXTERNAL, deployment.getSslRequired());
assertTrue(deployment.isUseResourceRoleMappings());
@@ -62,12 +68,16 @@ public class KeycloakDeploymentBuilderTest {
assertEquals(TokenStore.COOKIE, deployment.getTokenStore());
assertEquals("email", deployment.getPrincipalAttribute());
assertEquals(10, deployment.getTokenMinimumTimeToLive());
+ assertEquals(20, deployment.getMinTimeBetweenJwksRequests());
}
@Test
public void loadNoClientCredentials() throws Exception {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak-no-credentials.json"));
assertEquals(ClientIdAndSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId());
+
+ assertTrue(deployment.getPublicKeyLocator() instanceof JWKPublicKeyLocator);
+ assertEquals(10, deployment.getMinTimeBetweenJwksRequests());
}
@Test
diff --git a/adapters/oidc/adapter-core/src/test/resources/keycloak-no-credentials.json b/adapters/oidc/adapter-core/src/test/resources/keycloak-no-credentials.json
index 5f223ac75f..a3c4026c34 100644
--- a/adapters/oidc/adapter-core/src/test/resources/keycloak-no-credentials.json
+++ b/adapters/oidc/adapter-core/src/test/resources/keycloak-no-credentials.json
@@ -1,7 +1,6 @@
{
"realm": "demo",
"resource": "customer-portal",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "https://localhost:8443/auth",
"public-client": true,
"expose-token": true
diff --git a/adapters/oidc/adapter-core/src/test/resources/keycloak.json b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
index 7bf269f2c1..a8afd22cf2 100644
--- a/adapters/oidc/adapter-core/src/test/resources/keycloak.json
+++ b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
@@ -29,5 +29,6 @@
"register-node-period": 1000,
"token-store": "cookie",
"principal-attribute": "email",
- "token-minimum-time-to-live": 10
+ "token-minimum-time-to-live": 10,
+ "min-time-between-jwks-requests": 20
}
\ No newline at end of file
diff --git a/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java
index fc0e47fa50..c52a44af65 100644
--- a/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java
+++ b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java
@@ -20,6 +20,7 @@ package org.keycloak.adapters.installed;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.RSATokenVerifier;
+import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
@@ -213,7 +214,7 @@ public class KeycloakInstalled {
refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken();
- token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
+ token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
if (idTokenString != null) {
try {
JWSInput input = new JWSInput(idTokenString);
diff --git a/core/src/main/java/org/keycloak/RSATokenVerifier.java b/core/src/main/java/org/keycloak/RSATokenVerifier.java
index 27a430102b..562261de5e 100755
--- a/core/src/main/java/org/keycloak/RSATokenVerifier.java
+++ b/core/src/main/java/org/keycloak/RSATokenVerifier.java
@@ -38,6 +38,12 @@ public class RSATokenVerifier {
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
AccessToken token = toAccessToken(tokenString, realmKey);
+ tokenVerifications(token, realmUrl, checkActive, checkTokenType);
+
+ return token;
+ }
+
+ private static void tokenVerifications(AccessToken token, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
String user = token.getSubject();
if (user == null) {
throw new VerificationException("Token user was null.");
@@ -60,9 +66,9 @@ public class RSATokenVerifier {
throw new VerificationException("Token is not active.");
}
- return token;
}
+
public static AccessToken toAccessToken(String tokenString, PublicKey realmKey) throws VerificationException {
JWSInput input;
try {
@@ -81,6 +87,23 @@ public class RSATokenVerifier {
return token;
}
+
+ public static AccessToken verifyToken(JWSInput input, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
+ if (!isPublicKeyValid(input, realmKey)) throw new VerificationException("Invalid token signature.");
+
+ AccessToken token;
+ try {
+ token = input.readJsonContent(AccessToken.class);
+ } catch (JWSInputException e) {
+ throw new VerificationException("Couldn't parse token signature", e);
+ }
+
+ tokenVerifications(token, realmUrl, checkActive, checkTokenType);
+
+ return token;
+ }
+
+
private static boolean isPublicKeyValid(JWSInput input, PublicKey realmKey) throws VerificationException {
try {
return RSAProvider.verify(input, realmKey);
diff --git a/core/src/main/java/org/keycloak/constants/ServiceUrlConstants.java b/core/src/main/java/org/keycloak/constants/ServiceUrlConstants.java
index a21f26284b..36b4f1d2d6 100755
--- a/core/src/main/java/org/keycloak/constants/ServiceUrlConstants.java
+++ b/core/src/main/java/org/keycloak/constants/ServiceUrlConstants.java
@@ -30,5 +30,6 @@ public interface ServiceUrlConstants {
public static final String REALM_INFO_PATH = "/realms/{realm-name}";
public static final String CLIENTS_MANAGEMENT_REGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/register-node";
public static final String CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/unregister-node";
+ public static final String JWKS_URL = "/realms/{realm-name}/protocol/openid-connect/certs";
}
diff --git a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
index 7a31f72b6a..a20d253241 100755
--- a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
+++ b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
@@ -21,7 +21,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
import org.keycloak.common.util.Base64Url;
import org.keycloak.util.JsonSerialization;
-import java.io.InputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
@@ -66,8 +65,8 @@ public class JWKParser {
}
public PublicKey toPublicKey() {
- String algorithm = jwk.getKeyType();
- if (isAlgorithmSupported(algorithm)) {
+ String keyType = jwk.getKeyType();
+ if (isKeyTypeSupported(keyType)) {
BigInteger modulus = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.MODULUS).toString()));
BigInteger publicExponent = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.PUBLIC_EXPONENT).toString()));
@@ -77,12 +76,12 @@ public class JWKParser {
throw new RuntimeException(e);
}
} else {
- throw new RuntimeException("Unsupported algorithm " + algorithm);
+ throw new RuntimeException("Unsupported keyType " + keyType);
}
}
- public boolean isAlgorithmSupported(String algorithm) {
- return RSAPublicJWK.RSA.equals(algorithm);
+ public boolean isKeyTypeSupported(String keyType) {
+ return RSAPublicJWK.RSA.equals(keyType);
}
}
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
index 91fb5f0e9c..c4818b45c7 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
@@ -36,7 +36,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
"client-keystore", "client-keystore-password", "client-key-password",
"always-refresh-token",
"register-node-at-startup", "register-node-period", "token-store", "principal-attribute",
- "proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live",
+ "proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live", "min-time-between-jwks-requests",
"policy-enforcer"
})
public class AdapterConfig extends BaseAdapterConfig {
@@ -71,6 +71,8 @@ public class AdapterConfig extends BaseAdapterConfig {
protected Boolean turnOffChangeSessionIdOnLogin;
@JsonProperty("token-minimum-time-to-live")
protected int tokenMinimumTimeToLive = 0;
+ @JsonProperty("min-time-between-jwks-requests")
+ protected int minTimeBetweenJwksRequests = 10;
@JsonProperty("policy-enforcer")
protected PolicyEnforcerConfig policyEnforcerConfig;
@@ -216,4 +218,11 @@ public class AdapterConfig extends BaseAdapterConfig {
this.tokenMinimumTimeToLive = tokenMinimumTimeToLive;
}
+ public int getMinTimeBetweenJwksRequests() {
+ return minTimeBetweenJwksRequests;
+ }
+
+ public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) {
+ this.minTimeBetweenJwksRequests = minTimeBetweenJwksRequests;
+ }
}
diff --git a/core/src/main/java/org/keycloak/util/JWKSUtils.java b/core/src/main/java/org/keycloak/util/JWKSUtils.java
new file mode 100644
index 0000000000..72ffe91c86
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/JWKSUtils.java
@@ -0,0 +1,45 @@
+/*
+ * 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.util;
+
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.keycloak.jose.jwk.JSONWebKeySet;
+import org.keycloak.jose.jwk.JWK;
+import org.keycloak.jose.jwk.JWKParser;
+
+/**
+ * @author Marek Posolda
+ */
+public class JWKSUtils {
+
+ public static Map getKeysForUse(JSONWebKeySet keySet, JWK.Use requestedUse) {
+ Map result = new HashMap<>();
+
+ for (JWK jwk : keySet.getKeys()) {
+ JWKParser parser = JWKParser.create(jwk);
+ if (jwk.getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) {
+ result.put(jwk.getKeyId(), parser.toPublicKey());
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json
index a492837299..b5a1bbf797 100644
--- a/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json
@@ -1,6 +1,5 @@
{
"realm": "hello-world-authz",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "hello-world-authz-service",
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json
index c1dee24574..affafdd82c 100644
--- a/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json
@@ -1,6 +1,5 @@
{
"realm": "photoz",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external",
"resource" : "photoz-html5-client",
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
index 6849d0771b..9e06730fe3 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
@@ -1,6 +1,5 @@
{
"realm": "photoz",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "photoz-restful-api",
diff --git a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json
index eaffea8d19..f6b9c90927 100644
--- a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json
@@ -1,6 +1,5 @@
{
"realm": "servlet-authz",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external",
"resource" : "servlet-authz-app",
diff --git a/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json b/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json
index 4502199d70..9da7ed42e5 100644
--- a/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "basic-auth",
"resource" : "basic-auth-service",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required" : "external",
"enable-basic-auth" : "true",
diff --git a/examples/broker/facebook-authentication/src/main/webapp/keycloak.json b/examples/broker/facebook-authentication/src/main/webapp/keycloak.json
index 55446affdb..17743f663e 100644
--- a/examples/broker/facebook-authentication/src/main/webapp/keycloak.json
+++ b/examples/broker/facebook-authentication/src/main/webapp/keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "facebook-identity-provider-realm",
"resource" : "facebook-authentication",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required" : "external",
"public-client" : true
diff --git a/examples/broker/google-authentication/src/main/webapp/keycloak.json b/examples/broker/google-authentication/src/main/webapp/keycloak.json
index 93c25b4497..f05da2ff5e 100644
--- a/examples/broker/google-authentication/src/main/webapp/keycloak.json
+++ b/examples/broker/google-authentication/src/main/webapp/keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "google-identity-provider-realm",
"resource" : "google-authentication",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required" : "external",
"public-client" : true
diff --git a/examples/broker/twitter-authentication/src/main/webapp/keycloak.json b/examples/broker/twitter-authentication/src/main/webapp/keycloak.json
index 7243636390..2f745149d9 100644
--- a/examples/broker/twitter-authentication/src/main/webapp/keycloak.json
+++ b/examples/broker/twitter-authentication/src/main/webapp/keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "twitter-identity-provider-realm",
"resource" : "twitter-authentication",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required" : "external",
"public-client" : true
diff --git a/examples/cors/angular-product-app/src/main/webapp/keycloak.json b/examples/cors/angular-product-app/src/main/webapp/keycloak.json
index 40b35a1953..d68545945d 100755
--- a/examples/cors/angular-product-app/src/main/webapp/keycloak.json
+++ b/examples/cors/angular-product-app/src/main/webapp/keycloak.json
@@ -1,6 +1,5 @@
{
"realm" : "cors",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost-auth:8080/auth",
"ssl-required" : "external",
"resource" : "angular-cors-product",
diff --git a/examples/cors/database-service/src/main/webapp/WEB-INF/keycloak.json b/examples/cors/database-service/src/main/webapp/WEB-INF/keycloak.json
index 265049f22c..61da4087a3 100755
--- a/examples/cors/database-service/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/cors/database-service/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "cors",
"resource" : "cors-database-service",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost-auth:8080/auth",
"bearer-only" : true,
"ssl-required": "external",
diff --git a/examples/demo-template/angular-product-app/src/main/webapp/keycloak.json b/examples/demo-template/angular-product-app/src/main/webapp/keycloak.json
index 72ecb5be56..174053e4a1 100755
--- a/examples/demo-template/angular-product-app/src/main/webapp/keycloak.json
+++ b/examples/demo-template/angular-product-app/src/main/webapp/keycloak.json
@@ -1,6 +1,5 @@
{
"realm" : "demo",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "/auth",
"ssl-required" : "external",
"resource" : "angular-product",
diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/keycloak.json b/examples/demo-template/angular2-product-app/src/main/webapp/keycloak.json
index ddd1f2ec81..87a2ad64ef 100644
--- a/examples/demo-template/angular2-product-app/src/main/webapp/keycloak.json
+++ b/examples/demo-template/angular2-product-app/src/main/webapp/keycloak.json
@@ -1,6 +1,5 @@
{
"realm": "demo",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required": "external",
"resource": "angular2-product",
diff --git a/examples/demo-template/customer-app-cli/src/main/resources/META-INF/keycloak.json b/examples/demo-template/customer-app-cli/src/main/resources/META-INF/keycloak.json
index 51c8775c99..1c61433813 100644
--- a/examples/demo-template/customer-app-cli/src/main/resources/META-INF/keycloak.json
+++ b/examples/demo-template/customer-app-cli/src/main/resources/META-INF/keycloak.json
@@ -1,6 +1,5 @@
{
"realm" : "demo",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external",
"resource" : "customer-portal-cli",
diff --git a/examples/demo-template/customer-app-filter/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/customer-app-filter/src/main/webapp/WEB-INF/keycloak.json
index 14e56f543b..3766330ece 100755
--- a/examples/demo-template/customer-app-filter/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/demo-template/customer-app-filter/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm": "demo",
"resource": "customer-portal-filter",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required" : "external",
"expose-token": true,
diff --git a/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json b/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json
index 224c70b76f..4a60ffacd0 100644
--- a/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json
+++ b/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json
@@ -1,6 +1,5 @@
{
"realm" : "demo",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "/auth",
"ssl-required" : "external",
"resource" : "customer-portal-js",
diff --git a/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json
index c2241b3e91..20619097b5 100755
--- a/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm": "demo",
"resource": "customer-portal",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required" : "external",
"expose-token": true,
diff --git a/examples/demo-template/database-service/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/database-service/src/main/webapp/WEB-INF/keycloak.json
index cb938548dc..72e4903bc6 100755
--- a/examples/demo-template/database-service/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/demo-template/database-service/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "demo",
"resource" : "database-service",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"bearer-only" : true,
"ssl-required" : "external"
diff --git a/examples/demo-template/offline-access-app/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/offline-access-app/src/main/webapp/WEB-INF/keycloak.json
index dff976cf0e..600dcfa8a8 100644
--- a/examples/demo-template/offline-access-app/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/demo-template/offline-access-app/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm": "demo",
"resource": "offline-access-portal",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required" : "external",
"credentials": {
diff --git a/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json
index 0a86c041c7..109270101f 100755
--- a/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "demo",
"resource" : "product-portal",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "/auth",
"ssl-required" : "external",
"credentials" : {
diff --git a/examples/demo-template/service-account/src/main/java/org/keycloak/example/ProductServiceAccountServlet.java b/examples/demo-template/service-account/src/main/java/org/keycloak/example/ProductServiceAccountServlet.java
index e4d6a4033a..72ffd4959b 100644
--- a/examples/demo-template/service-account/src/main/java/org/keycloak/example/ProductServiceAccountServlet.java
+++ b/examples/demo-template/service-account/src/main/java/org/keycloak/example/ProductServiceAccountServlet.java
@@ -41,6 +41,7 @@ import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.keycloak.OAuth2Constants;
import org.keycloak.RSATokenVerifier;
+import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
@@ -163,7 +164,7 @@ public abstract class ProductServiceAccountServlet extends HttpServlet {
private void setTokens(HttpServletRequest req, KeycloakDeployment deployment, AccessTokenResponse tokenResponse) throws IOException, VerificationException {
String token = tokenResponse.getToken();
String refreshToken = tokenResponse.getRefreshToken();
- AccessToken tokenParsed = RSATokenVerifier.verifyToken(token, deployment.getRealmKey(), deployment.getRealmInfoUrl());
+ AccessToken tokenParsed = AdapterRSATokenVerifier.verifyToken(token, deployment);
req.getSession().setAttribute(TOKEN, token);
req.getSession().setAttribute(REFRESH_TOKEN, refreshToken);
req.getSession().setAttribute(TOKEN_PARSED, tokenParsed);
diff --git a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-secret.json b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-secret.json
index 7eec22a6c3..1a9322d96a 100644
--- a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-secret.json
+++ b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-secret.json
@@ -1,6 +1,5 @@
{
"realm" : "demo",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external",
"resource" : "product-sa-client",
diff --git a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json
index 3e90c34a30..3c99da7b42 100644
--- a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json
+++ b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json
@@ -1,6 +1,5 @@
{
"realm" : "demo",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external",
"resource" : "product-sa-client-jwt-auth",
diff --git a/examples/fuse/camel/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/examples/fuse/camel/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index a4796cc152..56550d681a 100644
--- a/examples/fuse/camel/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/examples/fuse/camel/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -26,7 +26,6 @@
-
diff --git a/examples/fuse/customer-app-fuse/src/main/webapp/WEB-INF/keycloak.json b/examples/fuse/customer-app-fuse/src/main/webapp/WEB-INF/keycloak.json
index b5d6b30d24..c7f61f68e3 100755
--- a/examples/fuse/customer-app-fuse/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/fuse/customer-app-fuse/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm": "demo",
"resource": "customer-portal",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required" : "external",
"credentials": {
diff --git a/examples/fuse/cxf-jaxrs/src/main/resources/WEB-INF/keycloak.json b/examples/fuse/cxf-jaxrs/src/main/resources/WEB-INF/keycloak.json
index f5d7e1a989..a8c4b752e1 100644
--- a/examples/fuse/cxf-jaxrs/src/main/resources/WEB-INF/keycloak.json
+++ b/examples/fuse/cxf-jaxrs/src/main/resources/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm": "demo",
"resource": "builtin-cxf-app",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required" : "external",
"credentials": {
diff --git a/examples/fuse/cxf-jaxws/src/main/resources/META-INF/spring/beans.xml b/examples/fuse/cxf-jaxws/src/main/resources/META-INF/spring/beans.xml
index 2d15ae017b..8a808c623c 100644
--- a/examples/fuse/cxf-jaxws/src/main/resources/META-INF/spring/beans.xml
+++ b/examples/fuse/cxf-jaxws/src/main/resources/META-INF/spring/beans.xml
@@ -32,7 +32,6 @@
-
diff --git a/examples/fuse/external-config/external-config-keycloak.json b/examples/fuse/external-config/external-config-keycloak.json
index 920e99a677..469da82a17 100644
--- a/examples/fuse/external-config/external-config-keycloak.json
+++ b/examples/fuse/external-config/external-config-keycloak.json
@@ -1,7 +1,6 @@
{
"realm": "demo",
"resource": "external-config",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required" : "external",
"credentials": {
diff --git a/examples/fuse/fuse-admin/keycloak-direct-access.json b/examples/fuse/fuse-admin/keycloak-direct-access.json
index 8e5ac0ff24..2441134231 100644
--- a/examples/fuse/fuse-admin/keycloak-direct-access.json
+++ b/examples/fuse/fuse-admin/keycloak-direct-access.json
@@ -1,7 +1,6 @@
{
"realm": "demo",
"resource": "ssh-jmx-admin-client",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"ssl-required" : "external",
"auth-server-url" : "http://localhost:8080/auth",
"credentials": {
diff --git a/examples/fuse/product-app-fuse/src/main/resources/WEB-INF/keycloak.json b/examples/fuse/product-app-fuse/src/main/resources/WEB-INF/keycloak.json
index 2a52d247f7..e90433ac51 100644
--- a/examples/fuse/product-app-fuse/src/main/resources/WEB-INF/keycloak.json
+++ b/examples/fuse/product-app-fuse/src/main/resources/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm": "demo",
"resource": "product-portal",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required" : "external",
"credentials": {
diff --git a/examples/js-console/src/main/webapp/keycloak.json b/examples/js-console/src/main/webapp/keycloak.json
index c0c04d5aee..cc4bab3394 100644
--- a/examples/js-console/src/main/webapp/keycloak.json
+++ b/examples/js-console/src/main/webapp/keycloak.json
@@ -1,6 +1,5 @@
{
"realm" : "example",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "/auth",
"ssl-required" : "external",
"resource" : "js-console",
diff --git a/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json b/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json
index db1223c6b2..7e9d91a7da 100644
--- a/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "kerberos-demo",
"resource" : "kerberos-app",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required" : "external",
"credentials": {
diff --git a/examples/ldap/src/main/webapp/WEB-INF/keycloak.json b/examples/ldap/src/main/webapp/WEB-INF/keycloak.json
index 84e1129a88..f43107b054 100644
--- a/examples/ldap/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/ldap/src/main/webapp/WEB-INF/keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "ldap-demo",
"resource" : "ldap-app",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth",
"ssl-required" : "external",
"credentials": {
diff --git a/examples/multi-tenant/src/main/resources/tenant1-keycloak.json b/examples/multi-tenant/src/main/resources/tenant1-keycloak.json
index 57be2774e7..34c17737b3 100644
--- a/examples/multi-tenant/src/main/resources/tenant1-keycloak.json
+++ b/examples/multi-tenant/src/main/resources/tenant1-keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "tenant1",
"resource" : "multi-tenant",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external",
"credentials" : {
diff --git a/examples/multi-tenant/src/main/resources/tenant2-keycloak.json b/examples/multi-tenant/src/main/resources/tenant2-keycloak.json
index 4f221dc66d..5877082e35 100644
--- a/examples/multi-tenant/src/main/resources/tenant2-keycloak.json
+++ b/examples/multi-tenant/src/main/resources/tenant2-keycloak.json
@@ -1,7 +1,6 @@
{
"realm" : "tenant2",
"resource" : "multi-tenant",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA0oJjgPQJhnVhOo51KauQGfLLreMFu64OJdKXRnfvAQJQTuKNwc5JrR63l/byyW1B6FgclABF818TtLvMCAkn4EuFwQZCZhg3x3+lFGiB/IzC6UAt4Bi0JQrTbdh83/U97GIPegvaDqiqEiQESEkbCZWxM6sh/34hQaAhCaFpMwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external",
"credentials" : {
diff --git a/server-spi/src/main/java/org/keycloak/models/Constants.java b/server-spi/src/main/java/org/keycloak/models/Constants.java
index 42982f6ace..edc9567e37 100755
--- a/server-spi/src/main/java/org/keycloak/models/Constants.java
+++ b/server-spi/src/main/java/org/keycloak/models/Constants.java
@@ -51,4 +51,7 @@ public interface Constants {
// Prefix for user attributes used in various "context"data maps
String USER_ATTRIBUTES_PREFIX = "user.attributes.";
+
+ // Indication to admin-rest-endpoint that realm keys should be re-generated
+ String GENERATE = "GENERATE";
}
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index ac285a8fe1..51b1f4e40f 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -820,7 +820,7 @@ public class RepresentationToModel {
realm.setUserFederationProviders(providerModels);
}
- if ("GENERATE".equals(rep.getPublicKey())) {
+ if (Constants.GENERATE.equals(rep.getPublicKey())) {
KeycloakModelUtils.generateRealmKeys(realm);
} else {
if (rep.getPrivateKey() != null && rep.getPublicKey() != null) {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
index 6fda3f0394..79a3919be5 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
@@ -51,7 +51,6 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
ClientManager.InstallationAdapterConfig rep = new ClientManager.InstallationAdapterConfig();
rep.setAuthServerUrl(baseUri.toString());
rep.setRealm(realm.getName());
- rep.setRealmKey(realm.getPublicKeyPem());
rep.setSslRequired(realm.getSslRequired().name().toLowerCase());
if (client.isPublicClient() && !client.isBearerOnly()) rep.setPublicClient(true);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java
index 33ab67787d..d0bc939eb2 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java
@@ -40,7 +40,6 @@ public class KeycloakOIDCJbossSubsystemClientInstallation implements ClientInsta
StringBuffer buffer = new StringBuffer();
buffer.append("\n");
buffer.append(" ").append(realm.getName()).append("\n");
- buffer.append(" ").append(realm.getPublicKeyPem()).append("\n");
buffer.append(" ").append(baseUri.toString()).append("\n");
if (client.isBearerOnly()){
buffer.append(" true\n");
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java
index d8f7fe72de..62d2c58039 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java
@@ -30,6 +30,8 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.util.JsonSerialization;
/**
+ * TODO: Merge with JWKSUtils from keycloak-core?
+ *
* @author Marek Posolda
*/
public class JWKSUtils {
@@ -44,7 +46,7 @@ public class JWKSUtils {
public static PublicKey getKeyForUse(JSONWebKeySet keySet, JWK.Use requestedUse) {
for (JWK jwk : keySet.getKeys()) {
JWKParser parser = JWKParser.create(jwk);
- if (parser.getJwk().getPublicKeyUse().equals(requestedUse.asString()) && parser.isAlgorithmSupported(jwk.getKeyType())) {
+ if (parser.getJwk().getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) {
return parser.toPublicKey();
}
}
diff --git a/services/src/main/java/org/keycloak/services/managers/ClientManager.java b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
index 7bdf5b1889..3de62db240 100644
--- a/services/src/main/java/org/keycloak/services/managers/ClientManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
@@ -265,7 +265,6 @@ public class ClientManager {
InstallationAdapterConfig rep = new InstallationAdapterConfig();
rep.setAuthServerUrl(baseUri.toString());
rep.setRealm(realmModel.getName());
- rep.setRealmKey(realmModel.getPublicKeyPem());
rep.setSslRequired(realmModel.getSslRequired().name().toLowerCase());
if (clientModel.isPublicClient() && !clientModel.isBearerOnly()) rep.setPublicClient(true);
@@ -286,7 +285,6 @@ public class ClientManager {
StringBuffer buffer = new StringBuffer();
buffer.append("\n");
buffer.append(" ").append(realmModel.getName()).append("\n");
- buffer.append(" ").append(realmModel.getPublicKeyPem()).append("\n");
buffer.append(" ").append(baseUri.toString()).append("\n");
if (clientModel.isBearerOnly()){
buffer.append(" true\n");
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 7d4922340d..1caa5fb207 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -35,6 +35,7 @@ import org.keycloak.events.admin.ResourceType;
import org.keycloak.exportimport.ClientDescriptionConverter;
import org.keycloak.exportimport.ClientDescriptionConverterFactory;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
@@ -281,7 +282,7 @@ public class RealmAdminResource {
logger.debug("updating realm: " + realm.getName());
try {
- if (!"GENERATE".equals(rep.getPublicKey()) && (rep.getPrivateKey() != null && rep.getPublicKey() != null)) {
+ if (!Constants.GENERATE.equals(rep.getPublicKey()) && (rep.getPrivateKey() != null && rep.getPublicKey() != null)) {
try {
KeyPairVerifier.verify(rep.getPrivateKey(), rep.getPublicKey());
} catch (VerificationException e) {
@@ -289,7 +290,7 @@ public class RealmAdminResource {
}
}
- if (!"GENERATE".equals(rep.getPublicKey()) && (rep.getCertificate() != null)) {
+ if (!Constants.GENERATE.equals(rep.getPublicKey()) && (rep.getCertificate() != null)) {
try {
X509Certificate cert = PemUtils.decodeCertificate(rep.getCertificate());
if (cert == null) {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java
index c282dff966..aad5bb889d 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java
@@ -27,8 +27,13 @@ import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.rotation.JWKPublicKeyLocator;
import org.keycloak.common.util.Time;
/**
@@ -38,6 +43,11 @@ import org.keycloak.common.util.Time;
*/
public class AdapterActionsFilter implements Filter {
+ public static final String TIME_OFFSET_PARAM = "timeOffset";
+ public static final String RESET_PUBLIC_KEY_PARAM = "resetPublicKey";
+
+ private static final Logger log = Logger.getLogger(AdapterActionsFilter.class);
+
@Override
public void init(FilterConfig filterConfig) throws ServletException {
@@ -45,16 +55,28 @@ public class AdapterActionsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest servletReq = (HttpServletRequest) request;
HttpServletResponse servletResp = (HttpServletResponse) response;
//Accept timeOffset as argument to enforce timeouts
- String timeOffsetParam = request.getParameter("timeOffset");
- if (timeOffsetParam != null && !timeOffsetParam.isEmpty()) {
- Time.setOffset(Integer.parseInt(timeOffsetParam));
- }
+ String timeOffsetParam = request.getParameter(TIME_OFFSET_PARAM);
+ String resetPublicKey = request.getParameter(RESET_PUBLIC_KEY_PARAM);
- // Continue request
- chain.doFilter(request, response);
+ if (timeOffsetParam != null && !timeOffsetParam.isEmpty()) {
+ int timeOffset = Integer.parseInt(timeOffsetParam);
+ log.infof("Time offset updated to %d for application %s", timeOffset, servletReq.getRequestURI());
+ Time.setOffset(timeOffset);
+ writeResponse(servletResp, "Offset set successfully");
+ } else if (resetPublicKey != null && !resetPublicKey.isEmpty()) {
+ AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext) request.getServletContext().getAttribute(AdapterDeploymentContext.class.getName());
+ KeycloakDeployment deployment = deploymentContext.resolveDeployment(null);
+ deployment.setPublicKeyLocator(new JWKPublicKeyLocator());
+ log.infof("Restarted publicKey locator for application %s", servletReq.getRequestURI());
+ writeResponse(servletResp, "PublicKeyLocator restarted successfully");
+ } else {
+ // Continue request
+ chain.doFilter(request, response);
+ }
}
@@ -64,8 +86,9 @@ public class AdapterActionsFilter implements Filter {
}
private void writeResponse(HttpServletResponse response, String responseText) throws IOException {
+ response.setContentType("text/html");
PrintWriter writer = response.getWriter();
- writer.println(responseText);
+ writer.println("" + responseText + "");
writer.flush();
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java
index d370dd049b..6cb1f37d5b 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java
@@ -23,6 +23,7 @@ import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
import org.keycloak.util.JsonSerialization;
+import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@@ -43,6 +44,8 @@ public abstract class AbstractShowTokensPage extends AbstractPageWithInjectedUrl
return JsonSerialization.readValue(accessToken.getText(), AccessToken.class);
} catch (IOException e) {
e.printStackTrace();
+ } catch (NoSuchElementException nsee) {
+ log.warn("No accessToken element found on the page");
}
return null;
@@ -53,7 +56,10 @@ public abstract class AbstractShowTokensPage extends AbstractPageWithInjectedUrl
return JsonSerialization.readValue(refreshToken.getText(), RefreshToken.class);
} catch (IOException e) {
e.printStackTrace();
+ } catch (NoSuchElementException nsee) {
+ log.warn("No idToken element found on the page");
}
+
return null;
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
index 9cd7625cc1..4af2988d1b 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
@@ -49,8 +49,6 @@ import static org.keycloak.testsuite.util.IOUtil.*;
*/
public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
- public static final String REALM_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";
-
protected final Logger log = org.jboss.logging.Logger.getLogger(this.getClass());
private final boolean authServerSslRequired = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required"));
@@ -129,7 +127,6 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
// ac.setRealmKey(null); // TODO verify if realm key is required for relative scneario
} else {
adapterConfig.setAuthServerUrl(getAuthServerContextRoot() + "/auth");
- adapterConfig.setRealmKey(REALM_KEY);
}
if ("true".equals(System.getProperty("app.server.ssl.required"))) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
index 127c863fa8..a40275f727 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
@@ -22,6 +22,7 @@ import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.By;
@@ -113,7 +114,7 @@ public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest {
protected void setAdapterAndServerTimeOffset(int timeOffset, String servletUri) {
setTimeOffset(timeOffset);
String timeOffsetUri = UriBuilder.fromUri(servletUri)
- .queryParam("timeOffset", timeOffset)
+ .queryParam(AdapterActionsFilter.TIME_OFFSET_PARAM, timeOffset)
.build().toString();
driver.navigate().to(timeOffsetUri);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
index 083867f514..02adb84429 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
@@ -28,6 +28,7 @@ import org.keycloak.OAuth2Constants;
import org.keycloak.common.Version;
import org.keycloak.common.util.Time;
import org.keycloak.constants.AdapterConstants;
+import org.keycloak.models.Constants;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.AccessToken;
@@ -36,8 +37,11 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter;
import org.keycloak.testsuite.adapter.page.*;
+import org.keycloak.testsuite.util.URLAssert;
import org.keycloak.testsuite.util.URLUtils;
+import org.keycloak.testsuite.util.WaitUtils;
import org.keycloak.util.BasicAuthHelper;
+import org.openqa.selenium.By;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
@@ -46,6 +50,8 @@ import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
import java.net.URI;
import java.util.List;
import java.util.Map;
@@ -166,6 +172,59 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
client.close();
}
+ @Test
+ public void testRealmKeyRotationWithNewKeyDownload() throws Exception {
+ // Login success first
+ tokenMinTTLPage.navigateTo();
+ testRealmLoginPage.form().waitForUsernameInputPresent();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlEquals(tokenMinTTLPage);
+
+ AccessToken token = tokenMinTTLPage.getAccessToken();
+ Assert.assertEquals("bburke@redhat.com", token.getPreferredUsername());
+
+ // Logout
+ String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder())
+ .queryParam(OAuth2Constants.REDIRECT_URI, tokenMinTTLPage.toString())
+ .build("demo").toString();
+ driver.navigate().to(logoutUri);
+
+ // Generate new realm key
+ RealmRepresentation realmRep = testRealmResource().toRepresentation();
+ String oldPublicKey = realmRep.getPublicKey();
+ String oldPrivateKey = realmRep.getPrivateKey();
+ realmRep.setPublicKey(Constants.GENERATE);
+ testRealmResource().update(realmRep);
+
+ // Try to login again. It should fail now
+ tokenMinTTLPage.navigateTo();
+ testRealmLoginPage.form().waitForUsernameInputPresent();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ URLAssert.assertCurrentUrlStartsWith(driver, tokenMinTTLPage.getInjectedUrl().toString());
+ assertNull(tokenMinTTLPage.getAccessToken());
+
+ String adapterActionsUrl = tokenMinTTLPage.toString() + "/unsecured/foo";
+ setAdapterAndServerTimeOffset(300, adapterActionsUrl);
+
+ // Try to login. Should work now due to realm key change
+ tokenMinTTLPage.navigateTo();
+ assertCurrentUrlEquals(tokenMinTTLPage);
+ token = tokenMinTTLPage.getAccessToken();
+ Assert.assertEquals("bburke@redhat.com", token.getPreferredUsername());
+ driver.navigate().to(logoutUri);
+
+ // Revert public keys change
+ String timeOffsetUri = UriBuilder.fromUri(adapterActionsUrl)
+ .queryParam(AdapterActionsFilter.RESET_PUBLIC_KEY_PARAM, "true")
+ .build().toString();
+ driver.navigate().to(timeOffsetUri);
+ WaitUtils.waitUntilElement(By.tagName("body")).is().visible();
+
+ setAdapterAndServerTimeOffset(0, adapterActionsUrl);
+ }
+
@Test
public void testLoginSSOAndLogout() {
// test login to customer-portal which does a bearer request to customer-db
@@ -444,6 +503,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
// Sets 5 minutes offset and assert access token will be still the same
setAdapterAndServerTimeOffset(300, tokenMinTTLPage.toString());
+ tokenMinTTLPage.navigateTo();
token = tokenMinTTLPage.getAccessToken();
int tokenIssued2 = token.getIssuedAt();
Assert.assertEquals(tokenIssued1, tokenIssued2);
@@ -451,6 +511,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
// Sets 9 minutes offset and assert access token will be refreshed (accessTokenTimeout is 10 minutes, token-min-ttl is 2 minutes. Hence 8 minutes or more should be sufficient)
setAdapterAndServerTimeOffset(540, tokenMinTTLPage.toString());
+ tokenMinTTLPage.navigateTo();
token = tokenMinTTLPage.getAccessToken();
int tokenIssued3 = token.getIssuedAt();
Assert.assertTrue(tokenIssued3 > tokenIssued1);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java
index fa6d0a8aed..b8c10b6dac 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java
@@ -85,7 +85,7 @@ public abstract class AbstractOfflineServletsAdapterTest extends AbstractServlet
String refreshTokenId = offlineTokenPage.getRefreshToken().getId();
setAdapterAndServerTimeOffset(9999);
-
+ offlineTokenPage.navigateTo();
assertCurrentUrlStartsWith(offlineTokenPage);
Assert.assertNotEquals(offlineTokenPage.getRefreshToken().getId(), refreshTokenId);
Assert.assertNotEquals(offlineTokenPage.getAccessToken().getId(), accessTokenId);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java
index fb03891e15..c7339ed12d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java
@@ -21,11 +21,11 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
-import org.keycloak.events.admin.OperationType;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
/**
* Test getting the installation/configuration files for OIDC and SAML.
@@ -81,7 +81,7 @@ public class InstallationTest extends AbstractClientTest {
private void assertOidcInstallationConfig(String config) {
RealmRepresentation realmRep = realmRep();
assertTrue(config.contains(realmRep.getId()));
- assertTrue(config.contains(realmRep.getPublicKey()));
+ assertFalse(config.contains(realmRep.getPublicKey()));
assertTrue(config.contains(authServerUrl()));
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
index 0a4fe4bea7..818a84a992 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
@@ -35,14 +35,11 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes
private ClientRepresentation client;
private ClientRepresentation client2;
private ClientRepresentation clientPublic;
- private String publicKey;
@Before
public void before() throws Exception {
super.before();
- publicKey = adminClient.realm(REALM_NAME).toRepresentation().getPublicKey();
-
client = new ClientRepresentation();
client.setEnabled(true);
client.setClientId("RegistrationAccessTokenTest");
@@ -92,7 +89,6 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes
assertEquals(1, config.getCredentials().size());
assertEquals(client.getSecret(), config.getCredentials().get("secret"));
- assertEquals(publicKey, config.getRealmKey());
assertEquals(client.getClientId(), config.getResource());
assertEquals(SslRequired.EXTERNAL.name().toLowerCase(), config.getSslRequired());
}
@@ -132,7 +128,6 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes
assertEquals(0, config.getCredentials().size());
- assertEquals(publicKey, config.getRealmKey());
assertEquals(clientPublic.getClientId(), config.getResource());
assertEquals(SslRequired.EXTERNAL.name().toLowerCase(), config.getSslRequired());
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json
index b75c1d5e65..32703f9a12 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json
@@ -1,10 +1,10 @@
{
"realm": "demo",
"resource": "customer-portal",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
- "auth-server-url": "http://localhost:8180/auth",
+ "auth-server-url": "http://localhostt:8180/auth",
"ssl-required" : "external",
"expose-token": true,
+ "min-time-between-jwks-requests": 120,
"credentials": {
"secret": "password"
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
index a934e97ec2..eecc24bac9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
@@ -1,9 +1,9 @@
{
"realm" : "demo",
"resource" : "input-portal",
- "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://${my.host.name}:8180/auth",
"ssl-required" : "external",
+ "min-time-between-jwks-requests": 120,
"credentials" : {
"secret": "password"
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/keycloak.json
index 85eb9a72ab..b86e018279 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/keycloak.json
@@ -1,11 +1,11 @@
{
"realm": "demo",
"resource": "token-min-ttl",
- "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8180/auth",
"ssl-required" : "external",
"credentials": {
"secret": "password"
},
- "token-minimum-time-to-live": 120
+ "token-minimum-time-to-live": 120,
+ "min-time-between-jwks-requests": 120
}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml
index f8110bc3cc..86d07f5ee6 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml
@@ -68,6 +68,12 @@
/error.html
+
+
+ Unsecured
+ /unsecured/*
+
+
KEYCLOAK