diff --git a/adapters/oidc/adapter-core/pom.xml b/adapters/oidc/adapter-core/pom.xml
index efcbf3a5d2..6d4e8a1921 100755
--- a/adapters/oidc/adapter-core/pom.xml
+++ b/adapters/oidc/adapter-core/pom.xml
@@ -21,7 +21,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../pom.xml
4.0.0
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 63ea79e65b..dd0d39ba78 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
@@ -342,6 +342,12 @@ public class AdapterDeploymentContext {
return delegate.getNotBefore();
}
+ @Override
+ public void updateNotBefore(int notBefore) {
+ delegate.setNotBefore(notBefore);
+ getPublicKeyLocator().reset(this);
+ }
+
@Override
public void setExposeToken(boolean exposeToken) {
delegate.setExposeToken(exposeToken);
@@ -446,6 +452,16 @@ public class AdapterDeploymentContext {
public int getMinTimeBetweenJwksRequests() {
return delegate.getMinTimeBetweenJwksRequests();
}
+
+ @Override
+ public int getPublicKeyCacheTtl() {
+ return delegate.getPublicKeyCacheTtl();
+ }
+
+ @Override
+ public void setPublicKeyCacheTtl(int publicKeyCacheTtl) {
+ delegate.setPublicKeyCacheTtl(publicKeyCacheTtl);
+ }
}
protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) {
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
index 3e0f36dee7..b7877964a0 100644
--- 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
@@ -46,14 +46,7 @@ public class HttpAdapterUtils {
}
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);
+ return JsonSerialization.readValue(is, clazz);
} finally {
try {
is.close();
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 1071b710fc..b9ee4c6f05 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
@@ -81,6 +81,7 @@ public class KeycloakDeployment {
protected volatile int notBefore;
protected int tokenMinimumTimeToLive;
protected int minTimeBetweenJwksRequests;
+ protected int publicKeyCacheTtl;
private PolicyEnforcer policyEnforcer;
public KeycloakDeployment() {
@@ -328,6 +329,11 @@ public class KeycloakDeployment {
this.notBefore = notBefore;
}
+ public void updateNotBefore(int notBefore) {
+ this.notBefore = notBefore;
+ getPublicKeyLocator().reset(this);
+ }
+
public boolean isAlwaysRefreshToken() {
return alwaysRefreshToken;
}
@@ -384,6 +390,14 @@ public class KeycloakDeployment {
this.minTimeBetweenJwksRequests = minTimeBetweenJwksRequests;
}
+ public int getPublicKeyCacheTtl() {
+ return publicKeyCacheTtl;
+ }
+
+ public void setPublicKeyCacheTtl(int publicKeyCacheTtl) {
+ this.publicKeyCacheTtl = publicKeyCacheTtl;
+ }
+
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 f6c6f5ea12..85b19ca538 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
@@ -105,6 +105,7 @@ public class KeycloakDeploymentBuilder {
deployment.setRegisterNodePeriod(adapterConfig.getRegisterNodePeriod());
deployment.setTokenMinimumTimeToLive(adapterConfig.getTokenMinimumTimeToLive());
deployment.setMinTimeBetweenJwksRequests(adapterConfig.getMinTimeBetweenJwksRequests());
+ deployment.setPublicKeyCacheTtl(adapterConfig.getPublicKeyCacheTtl());
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 109361f1df..e8f534492a 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
@@ -357,7 +357,7 @@ public class OAuthRequestAuthenticator {
return challenge(403, OIDCAuthenticationError.Reason.INVALID_TOKEN, null);
}
if (tokenResponse.getNotBeforePolicy() > deployment.getNotBefore()) {
- deployment.setNotBefore(tokenResponse.getNotBeforePolicy());
+ deployment.updateNotBefore(tokenResponse.getNotBeforePolicy());
}
if (token.getIssuedAt() < deployment.getNotBefore()) {
log.error("Stale token");
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 9a291c071b..b4d017bc57 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
@@ -155,7 +155,7 @@ public class PreAuthActionsHandler {
} else {
log.debugf("logout of all sessions for application '%s'", action.getResource());
if (action.getNotBefore() > deployment.getNotBefore()) {
- deployment.setNotBefore(action.getNotBefore());
+ deployment.updateNotBefore(action.getNotBefore());
}
userSessionManagement.logoutAll();
}
@@ -177,7 +177,7 @@ public class PreAuthActionsHandler {
}
PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
if (!validateAction(action)) return;
- deployment.setNotBefore(action.getNotBefore());
+ deployment.updateNotBefore(action.getNotBefore());
} catch (Exception e) {
throw new RuntimeException(e);
}
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 39a3f1e59f..c70bce1261 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
@@ -144,7 +144,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
}
if (response.getNotBeforePolicy() > deployment.getNotBefore()) {
- deployment.setNotBefore(response.getNotBeforePolicy());
+ deployment.updateNotBefore(response.getNotBeforePolicy());
}
this.token = token;
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
index 2aa51a44c3..9e285a2c8c 100644
--- 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
@@ -37,4 +37,9 @@ public class HardcodedPublicKeyLocator implements PublicKeyLocator {
public PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
return publicKey;
}
+
+ @Override
+ public void reset(KeycloakDeployment deployment) {
+
+ }
}
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
index 9305f32a49..22c6d7dc21 100644
--- 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
@@ -25,7 +25,6 @@ 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;
import java.security.PublicKey;
@@ -48,30 +47,43 @@ public class JWKPublicKeyLocator implements PublicKeyLocator {
@Override
public PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
int minTimeBetweenRequests = deployment.getMinTimeBetweenJwksRequests();
+ int publicKeyCacheTtl = deployment.getPublicKeyCacheTtl();
+ int currentTime = Time.currentTime();
// Check if key is in cache.
- PublicKey publicKey = currentKeys.get(kid);
+ PublicKey publicKey = lookupCachedKey(publicKeyCacheTtl, currentTime, 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 {
- log.debugf("Won't send request to realm jwks url. Last request time was %d", lastRequestTime);
- }
+ synchronized (this) {
+ currentTime = Time.currentTime();
+ if (currentTime > lastRequestTime + minTimeBetweenRequests) {
+ sendRequest(deployment);
+ lastRequestTime = currentTime;
+ } else {
+ log.debugf("Won't send request to realm jwks url. Last request time was %d", lastRequestTime);
}
+
+ return lookupCachedKey(publicKeyCacheTtl, currentTime, kid);
}
+ }
- return currentKeys.get(kid);
+ @Override
+ public void reset(KeycloakDeployment deployment) {
+ sendRequest(deployment);
+ lastRequestTime = Time.currentTime();
+ }
+
+
+ private PublicKey lookupCachedKey(int publicKeyCacheTtl, int currentTime, String kid) {
+ if (lastRequestTime + publicKeyCacheTtl > currentTime) {
+ return currentKeys.get(kid);
+ } else {
+ return null;
+ }
}
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
index 3efd90a3b7..096f75f042 100644
--- 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
@@ -34,4 +34,11 @@ public interface PublicKeyLocator {
*/
PublicKey getPublicKey(String kid, KeycloakDeployment deployment);
+ /**
+ * Reset the state of locator (eg. clear the cached keys)
+ *
+ * @param deployment
+ */
+ void reset(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 77eb475bf6..233c1ed2f0 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
@@ -69,6 +69,7 @@ public class KeycloakDeploymentBuilderTest {
assertEquals("email", deployment.getPrincipalAttribute());
assertEquals(10, deployment.getTokenMinimumTimeToLive());
assertEquals(20, deployment.getMinTimeBetweenJwksRequests());
+ assertEquals(120, deployment.getPublicKeyCacheTtl());
}
@Test
@@ -78,6 +79,7 @@ public class KeycloakDeploymentBuilderTest {
assertTrue(deployment.getPublicKeyLocator() instanceof JWKPublicKeyLocator);
assertEquals(10, deployment.getMinTimeBetweenJwksRequests());
+ assertEquals(86400, deployment.getPublicKeyCacheTtl());
}
@Test
diff --git a/adapters/oidc/adapter-core/src/test/resources/keycloak.json b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
index a8afd22cf2..9f0a204826 100644
--- a/adapters/oidc/adapter-core/src/test/resources/keycloak.json
+++ b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
@@ -30,5 +30,6 @@
"token-store": "cookie",
"principal-attribute": "email",
"token-minimum-time-to-live": 10,
- "min-time-between-jwks-requests": 20
+ "min-time-between-jwks-requests": 20,
+ "public-key-cache-ttl": 120
}
\ No newline at end of file
diff --git a/adapters/oidc/as7-eap6/as7-adapter-spi/pom.xml b/adapters/oidc/as7-eap6/as7-adapter-spi/pom.xml
index 1f64c36fca..fa0c29bf41 100755
--- a/adapters/oidc/as7-eap6/as7-adapter-spi/pom.xml
+++ b/adapters/oidc/as7-eap6/as7-adapter-spi/pom.xml
@@ -21,7 +21,7 @@
keycloak-as7-integration-pom
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../pom.xml
4.0.0
diff --git a/adapters/oidc/as7-eap6/as7-adapter/pom.xml b/adapters/oidc/as7-eap6/as7-adapter/pom.xml
index 5f21d6d427..67ad24b51b 100755
--- a/adapters/oidc/as7-eap6/as7-adapter/pom.xml
+++ b/adapters/oidc/as7-eap6/as7-adapter/pom.xml
@@ -21,7 +21,7 @@
keycloak-as7-integration-pom
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../pom.xml
4.0.0
diff --git a/adapters/oidc/as7-eap6/as7-subsystem/pom.xml b/adapters/oidc/as7-eap6/as7-subsystem/pom.xml
index 972903fcde..4b3369d65b 100755
--- a/adapters/oidc/as7-eap6/as7-subsystem/pom.xml
+++ b/adapters/oidc/as7-eap6/as7-subsystem/pom.xml
@@ -21,7 +21,7 @@
org.keycloak
keycloak-as7-integration-pom
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../pom.xml
diff --git a/adapters/oidc/as7-eap6/pom.xml b/adapters/oidc/as7-eap6/pom.xml
index 626c1a10f3..e8c50f5320 100755
--- a/adapters/oidc/as7-eap6/pom.xml
+++ b/adapters/oidc/as7-eap6/pom.xml
@@ -20,7 +20,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../pom.xml
Keycloak AS7 / JBoss EAP 6 Integration
diff --git a/adapters/oidc/installed/pom.xml b/adapters/oidc/installed/pom.xml
index 2cff21abc9..1e88bae934 100755
--- a/adapters/oidc/installed/pom.xml
+++ b/adapters/oidc/installed/pom.xml
@@ -21,7 +21,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../pom.xml
4.0.0
diff --git a/adapters/oidc/jaxrs-oauth-client/pom.xml b/adapters/oidc/jaxrs-oauth-client/pom.xml
index feca253cab..d91119251b 100755
--- a/adapters/oidc/jaxrs-oauth-client/pom.xml
+++ b/adapters/oidc/jaxrs-oauth-client/pom.xml
@@ -21,7 +21,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../pom.xml
4.0.0
diff --git a/adapters/oidc/jetty/jetty-core/pom.xml b/adapters/oidc/jetty/jetty-core/pom.xml
index d6de06173d..0a626191a7 100755
--- a/adapters/oidc/jetty/jetty-core/pom.xml
+++ b/adapters/oidc/jetty/jetty-core/pom.xml
@@ -21,7 +21,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../../pom.xml
4.0.0
diff --git a/adapters/oidc/jetty/jetty8.1/pom.xml b/adapters/oidc/jetty/jetty8.1/pom.xml
index 5d9bfc32c3..b966d60e55 100755
--- a/adapters/oidc/jetty/jetty8.1/pom.xml
+++ b/adapters/oidc/jetty/jetty8.1/pom.xml
@@ -21,7 +21,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../../pom.xml
4.0.0
diff --git a/adapters/oidc/jetty/jetty9.1/pom.xml b/adapters/oidc/jetty/jetty9.1/pom.xml
index dbe08dd775..d017e24dda 100755
--- a/adapters/oidc/jetty/jetty9.1/pom.xml
+++ b/adapters/oidc/jetty/jetty9.1/pom.xml
@@ -21,7 +21,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../../pom.xml
4.0.0
diff --git a/adapters/oidc/jetty/jetty9.2/pom.xml b/adapters/oidc/jetty/jetty9.2/pom.xml
index 430a02d0fa..20e7afaa48 100755
--- a/adapters/oidc/jetty/jetty9.2/pom.xml
+++ b/adapters/oidc/jetty/jetty9.2/pom.xml
@@ -21,7 +21,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../../pom.xml
4.0.0
diff --git a/adapters/oidc/jetty/jetty9.3/pom.xml b/adapters/oidc/jetty/jetty9.3/pom.xml
index 9596a00bf0..b1ec1ff804 100644
--- a/adapters/oidc/jetty/jetty9.3/pom.xml
+++ b/adapters/oidc/jetty/jetty9.3/pom.xml
@@ -21,7 +21,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../../pom.xml
4.0.0
diff --git a/adapters/oidc/jetty/pom.xml b/adapters/oidc/jetty/pom.xml
index 6933fe78ab..d4b2f4bfda 100755
--- a/adapters/oidc/jetty/pom.xml
+++ b/adapters/oidc/jetty/pom.xml
@@ -20,7 +20,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../pom.xml
Keycloak Jetty Integration
diff --git a/adapters/oidc/js/pom.xml b/adapters/oidc/js/pom.xml
index 5d1f812101..026d8e4aa2 100755
--- a/adapters/oidc/js/pom.xml
+++ b/adapters/oidc/js/pom.xml
@@ -21,7 +21,7 @@
keycloak-parent
org.keycloak
- 2.4.1.Final-SNAPSHOT
+ 2.5.0.Final-SNAPSHOT
../../../pom.xml
4.0.0
diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js
index ba27ed0cfc..70b318ec4b 100755
--- a/adapters/oidc/js/src/main/resources/keycloak.js
+++ b/adapters/oidc/js/src/main/resources/keycloak.js
@@ -846,14 +846,15 @@
kc.clearToken();
}
- for (var i = loginIframe.callbackList.length - 1; i >= 0; --i) {
- var promise = loginIframe.callbackList[i];
+ var callbacks = loginIframe.callbackList.splice(0, loginIframe.callbackList.length);
+
+ for (var i = callbacks.length - 1; i >= 0; --i) {
+ var promise = callbacks[i];
if (event.data == "unchanged") {
promise.setSuccess();
} else {
promise.setError();
}
- loginIframe.callbackList.splice(i, 1);
}
};
diff --git a/adapters/oidc/js/src/main/resources/login-status-iframe.html b/adapters/oidc/js/src/main/resources/login-status-iframe.html
index 6bea92ade4..f941663e4f 100755
--- a/adapters/oidc/js/src/main/resources/login-status-iframe.html
+++ b/adapters/oidc/js/src/main/resources/login-status-iframe.html
@@ -35,14 +35,14 @@
req.open('GET', url, true);
req.onreadystatechange = function () {
- if (req.readyState == 4) {
- if (req.status == 204) {
+ if (req.readyState === 4) {
+ if (req.status === 204) {
init = {
clientId: clientId,
origin: origin
}
callback('unchanged');
- } else if (req.status = 404) {
+ } else if (req.status === 404) {
callback('changed');
} else {
callback('error');
@@ -52,8 +52,8 @@
req.send();
} else {
- if (clientId == init.clientId && origin == init.origin) {
- if (sessionState == cookie) {
+ if (clientId === init.clientId && origin === init.origin) {
+ if (sessionState === cookie) {
callback('unchanged');
} else {
callback('changed');
@@ -71,7 +71,7 @@
for(var i=0; i