From 54b40d31b66435c016174be1728278189b1dc7d9 Mon Sep 17 00:00:00 2001 From: graziang Date: Wed, 6 Mar 2024 18:12:03 +0100 Subject: [PATCH] Revoked token cache expiration fix Added 1 second to the duration of the cache for revoked tokens to prevent them from still being valid for 1 second after the expiration date of the access token. Closes #26113 Signed-off-by: graziang --- .../endpoints/TokenRevocationEndpoint.java | 2 +- .../testsuite/oauth/TokenRevocationTest.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java index f81f28dc68..b0cb3fab33 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java @@ -266,7 +266,7 @@ public class TokenRevocationEndpoint { private void revokeAccessToken() { SingleUseObjectProvider singleUseStore = session.singleUseObjects(); int currentTime = Time.currentTime(); - long lifespanInSecs = Math.max(token.getExp() - currentTime, 10); + long lifespanInSecs = Math.max(token.getExp() - currentTime + 1, 10); singleUseStore.put(token.getId() + SingleUseObjectProvider.REVOKED_KEY, lifespanInSecs, Collections.emptyMap()); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenRevocationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenRevocationTest.java index cad12776e7..6d7e465ebe 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenRevocationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenRevocationTest.java @@ -69,6 +69,7 @@ import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient.AccessTokenResponse; import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.UserInfoClientUtil; +import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule; import org.keycloak.util.JsonSerialization; /** @@ -84,6 +85,9 @@ public class TokenRevocationTest extends AbstractKeycloakTest { @Rule public AssertEvents events = new AssertEvents(this); + @Rule + public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this); + @Override public void beforeAbstractKeycloakTest() throws Exception { super.beforeAbstractKeycloakTest(); @@ -171,6 +175,23 @@ public class TokenRevocationTest extends AbstractKeycloakTest { isAccessTokenDisabled(tokenResponse.getAccessToken(), "test-app"); } + @Test + public void testRevokedAccessTokenCacheLifespan() throws Exception { + oauth.clientId("test-app"); + OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password"); + + isTokenEnabled(tokenResponse, "test-app"); + + CloseableHttpResponse response = oauth.doTokenRevoke(tokenResponse.getAccessToken(), "access_token", "password"); + assertThat(response, Matchers.statusCodeIsHC(Status.OK)); + + setTimeOffset(adminClient.realm(oauth.getRealm()).toRepresentation().getAccessTokenLifespan()); + + isAccessTokenDisabled(tokenResponse.getAccessToken(), "test-app"); + + setTimeOffset(0); + } + @Test public void testRevokeOfflineToken() throws Exception { oauth.scope(OAuth2Constants.OFFLINE_ACCESS);