diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java index 194b0a010a..8d9f6381bc 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java @@ -45,7 +45,6 @@ import org.keycloak.testsuite.AbstractAuthTest; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.admin.ApiUtil; -import org.keycloak.testsuite.federation.storage.ComponentExportImportTest; import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.runonserver.RunOnServerDeployment; import org.keycloak.testsuite.util.ClientManager; @@ -54,6 +53,7 @@ import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.RealmManager; import org.keycloak.testsuite.util.TokenSignatureUtil; import org.keycloak.testsuite.util.UserManager; +import org.keycloak.testsuite.util.WaitUtils; import org.keycloak.util.BasicAuthHelper; import javax.ws.rs.client.Client; @@ -245,6 +245,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest { OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password"); String accessTokenString = tokenResponse.getAccessToken(); + setTimeOffset(2); OAuthClient.AccessTokenResponse response = oauth.doRefreshTokenRequest(accessTokenString, "password"); Assert.assertNotEquals(200, response.getStatusCode()); @@ -274,6 +275,8 @@ public class RefreshTokenTest extends AbstractKeycloakTest { events.expectRefresh(refreshToken1.getId(), sessionId).assertEvent(); + setTimeOffset(4); + OAuthClient.AccessTokenResponse response3 = oauth.doRefreshTokenRequest(response1.getRefreshToken(), "password"); assertEquals(200, response3.getStatusCode()); @@ -313,12 +316,15 @@ public class RefreshTokenTest extends AbstractKeycloakTest { events.expectRefresh(refreshToken1.getId(), sessionId).assertEvent(); + setTimeOffset(4); + OAuthClient.AccessTokenResponse response3 = oauth.doRefreshTokenRequest(response1.getRefreshToken(), "password"); assertEquals(400, response3.getStatusCode()); events.expectRefresh(refreshToken1.getId(), sessionId).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent(); + setTimeOffset(6); oauth.doRefreshTokenRequest(response2.getRefreshToken(), "password"); events.expectRefresh(refreshToken2.getId(), sessionId).assertEvent(); @@ -369,6 +375,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest { events.expectRefresh(initialRefreshToken.getId(), sessionId).assertEvent(); + setTimeOffset(6); // Token reused twice, became invalid. OAuthClient.AccessTokenResponse responseSecondReuse = oauth.doRefreshTokenRequest(initialResponse.getRefreshToken(), "password"); @@ -377,6 +384,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest { events.expectRefresh(initialRefreshToken.getId(), sessionId).removeDetail(Details.TOKEN_ID) .removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent(); + setTimeOffset(8); // Refresh token from first use became invalid. OAuthClient.AccessTokenResponse responseUseOfInvalidatedRefreshToken = oauth.doRefreshTokenRequest(responseFirstUse.getRefreshToken(), "password"); @@ -386,6 +394,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest { events.expectRefresh(newTokenFirstUse.getId(), sessionId).removeDetail(Details.TOKEN_ID) .removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent(); + setTimeOffset(10); // Refresh token from reuse is still valid. OAuthClient.AccessTokenResponse responseUseOfValidRefreshToken = oauth.doRefreshTokenRequest(responseFirstReuse.getRefreshToken(), "password"); @@ -522,6 +531,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest { try { ClientManager.realm(adminClient.realm("test")).clientId(oauth.getClientId()).enabled(false); + setTimeOffset(2); response = oauth.doRefreshTokenRequest(refreshTokenString, "password"); assertEquals(400, response.getStatusCode()); @@ -550,6 +560,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest { testingClient.testing().removeUserSession("test", sessionId); + setTimeOffset(2); tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password"); assertEquals(400, tokenResponse.getStatusCode()); @@ -580,10 +591,12 @@ public class RefreshTokenTest extends AbstractKeycloakTest { String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); tokenResponse2 = oauth.doAccessTokenRequest(code, "password"); + setTimeOffset(4); // Now try refresh with the original refreshToken1 created in logged-out userSession. It should fail OAuthClient.AccessTokenResponse responseReuseExceeded = oauth.doRefreshTokenRequest(refreshToken1, "password"); assertEquals(400, responseReuseExceeded.getStatusCode()); + setTimeOffset(6); // Finally try with valid refresh token responseReuseExceeded = oauth.doRefreshTokenRequest(tokenResponse2.getRefreshToken(), "password"); assertEquals(200, responseReuseExceeded.getStatusCode()); @@ -594,6 +607,8 @@ public class RefreshTokenTest extends AbstractKeycloakTest { String refreshToken1 = loginAndForceNewLoginPage(); adminClient.realm("test").logoutAll(); + // Must wait for server to execute the request. Sometimes, there is issue with the execution and another tests failed, because of this. + WaitUtils.pause(500); events.clear(); @@ -609,10 +624,14 @@ public class RefreshTokenTest extends AbstractKeycloakTest { String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); tokenResponse2 = oauth.doAccessTokenRequest(code, "password"); + setTimeOffset(4); + // Now try refresh with the original refreshToken1 created in logged-out userSession. It should fail OAuthClient.AccessTokenResponse responseReuseExceeded = oauth.doRefreshTokenRequest(refreshToken1, "password"); assertEquals(400, responseReuseExceeded.getStatusCode()); + setTimeOffset(6); + // Finally try with valid refresh token responseReuseExceeded = oauth.doRefreshTokenRequest(tokenResponse2.getRefreshToken(), "password"); assertEquals(200, responseReuseExceeded.getStatusCode()); @@ -640,10 +659,14 @@ public class RefreshTokenTest extends AbstractKeycloakTest { String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); tokenResponse2 = oauth.doAccessTokenRequest(code, "password"); + setTimeOffset(4); + // Now try refresh with the original refreshToken1 created in logged-out userSession. It should fail OAuthClient.AccessTokenResponse responseReuseExceeded = oauth.doRefreshTokenRequest(refreshToken1, "password"); assertEquals(400, responseReuseExceeded.getStatusCode()); + setTimeOffset(6); + // Finally try with valid refresh token responseReuseExceeded = oauth.doRefreshTokenRequest(tokenResponse2.getRefreshToken(), "password"); assertEquals(200, responseReuseExceeded.getStatusCode()); @@ -689,36 +712,39 @@ public class RefreshTokenTest extends AbstractKeycloakTest { RealmResource realmResource = adminClient.realm("test"); int lastAccessTokenLifespan = realmResource.toRepresentation().getAccessTokenLifespan(); - RealmManager.realm(realmResource).accessTokenLifespan(100000); - - setTimeOffset(4); - tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password"); - - next = testingClient.testing().getLastSessionRefresh("test", sessionId, false); - - // lastSEssionRefresh should be updated because access code lifespan is higher than sso idle timeout - Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 50))); - int originalIdle = realmResource.toRepresentation().getSsoSessionIdleTimeout(); - RealmManager.realm(realmResource).ssoSessionIdleTimeout(1); - events.clear(); - // Needs to add some additional time due the tollerance allowed by IDLE_TIMEOUT_WINDOW_SECONDS - setTimeOffset(6 + SessionTimeoutHelper.IDLE_TIMEOUT_WINDOW_SECONDS); - tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password"); + try { + RealmManager.realm(realmResource).accessTokenLifespan(100000); - // test idle timeout - assertEquals(400, tokenResponse.getStatusCode()); - assertNull(tokenResponse.getAccessToken()); - assertNull(tokenResponse.getRefreshToken()); + setTimeOffset(4); + tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password"); - events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN); + next = testingClient.testing().getLastSessionRefresh("test", sessionId, false); - RealmManager.realm(realmResource).ssoSessionIdleTimeout(originalIdle).accessTokenLifespan(lastAccessTokenLifespan); + // lastSEssionRefresh should be updated because access code lifespan is higher than sso idle timeout + Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 50))); - events.clear(); + RealmManager.realm(realmResource).ssoSessionIdleTimeout(1); + + events.clear(); + // Needs to add some additional time due the tollerance allowed by IDLE_TIMEOUT_WINDOW_SECONDS + setTimeOffset(6 + SessionTimeoutHelper.IDLE_TIMEOUT_WINDOW_SECONDS); + tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password"); + + // test idle timeout + assertEquals(400, tokenResponse.getStatusCode()); + assertNull(tokenResponse.getAccessToken()); + assertNull(tokenResponse.getRefreshToken()); + + events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN); + + } finally { + RealmManager.realm(realmResource).ssoSessionIdleTimeout(originalIdle).accessTokenLifespan(lastAccessTokenLifespan); + events.clear(); + setTimeOffset(0); + } - setTimeOffset(0); } @Test @@ -796,23 +822,23 @@ public class RefreshTokenTest extends AbstractKeycloakTest { RealmResource realmResource = adminClient.realm("test"); Integer maxLifespan = realmResource.toRepresentation().getSsoSessionMaxLifespan(); - RealmManager.realm(realmResource).ssoSessionMaxLifespan(1); + try { + RealmManager.realm(realmResource).ssoSessionMaxLifespan(1); - setTimeOffset(1); + setTimeOffset(2); - tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password"); + tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password"); - assertEquals(400, tokenResponse.getStatusCode()); - assertNull(tokenResponse.getAccessToken()); - assertNull(tokenResponse.getRefreshToken()); + assertEquals(400, tokenResponse.getStatusCode()); + assertNull(tokenResponse.getAccessToken()); + assertNull(tokenResponse.getRefreshToken()); - RealmManager.realm(realmResource).ssoSessionMaxLifespan(maxLifespan); - - events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN); - - events.clear(); - - setTimeOffset(0); + events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN); + } finally { + RealmManager.realm(realmResource).ssoSessionMaxLifespan(maxLifespan); + events.clear(); + resetTimeOffset(); + } } /** @@ -869,57 +895,59 @@ public class RefreshTokenTest extends AbstractKeycloakTest { @Test public void testCheckSsl() throws Exception { Client client = ClientBuilder.newClient(); - UriBuilder builder = UriBuilder.fromUri(AUTH_SERVER_ROOT); - URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test"); - WebTarget grantTarget = client.target(grantUri); - builder = UriBuilder.fromUri(AUTH_SERVER_ROOT); - URI uri = OIDCLoginProtocolService.tokenUrl(builder).build("test"); - WebTarget refreshTarget = client.target(uri); + try { + UriBuilder builder = UriBuilder.fromUri(AUTH_SERVER_ROOT); + URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test"); + WebTarget grantTarget = client.target(grantUri); + builder = UriBuilder.fromUri(AUTH_SERVER_ROOT); + URI uri = OIDCLoginProtocolService.tokenUrl(builder).build("test"); + WebTarget refreshTarget = client.target(uri); - String refreshToken = null; - { - Response response = executeGrantAccessTokenRequest(grantTarget); - assertEquals(200, response.getStatus()); - org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); - refreshToken = tokenResponse.getRefreshToken(); - response.close(); - } - - { - Response response = executeRefreshToken(refreshTarget, refreshToken); - assertEquals(200, response.getStatus()); - org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); - refreshToken = tokenResponse.getRefreshToken(); - response.close(); - } - - if (!AUTH_SERVER_SSL_REQUIRED) { // test checkSsl - RealmResource realmResource = adminClient.realm("test"); + String refreshToken = null; { - RealmManager.realm(realmResource).sslRequired(SslRequired.ALL.toString()); + Response response = executeGrantAccessTokenRequest(grantTarget); + assertEquals(200, response.getStatus()); + org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); + refreshToken = tokenResponse.getRefreshToken(); + response.close(); } - Response response = executeRefreshToken(refreshTarget, refreshToken); - assertEquals(403, response.getStatus()); - response.close(); + { + Response response = executeRefreshToken(refreshTarget, refreshToken); + assertEquals(200, response.getStatus()); + org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); + refreshToken = tokenResponse.getRefreshToken(); + response.close(); + } + + if (!AUTH_SERVER_SSL_REQUIRED) { // test checkSsl + RealmResource realmResource = adminClient.realm("test"); + { + RealmManager.realm(realmResource).sslRequired(SslRequired.ALL.toString()); + } + + Response response = executeRefreshToken(refreshTarget, refreshToken); + assertEquals(403, response.getStatus()); + response.close(); + + { + RealmManager.realm(realmResource).sslRequired(SslRequired.EXTERNAL.toString()); + } + } { - RealmManager.realm(realmResource).sslRequired(SslRequired.EXTERNAL.toString()); + Response response = executeRefreshToken(refreshTarget, refreshToken); + assertEquals(200, response.getStatus()); + org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); + refreshToken = tokenResponse.getRefreshToken(); + response.close(); } + } finally { + client.close(); + resetTimeOffset(); + events.clear(); } - { - Response response = executeRefreshToken(refreshTarget, refreshToken); - assertEquals(200, response.getStatus()); - org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); - refreshToken = tokenResponse.getRefreshToken(); - response.close(); - } - - - client.close(); - events.clear(); - } @Test @@ -941,6 +969,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest { try { UserManager.realm(adminClient.realm("test")).username("test-user@localhost").enabled(false); + setTimeOffset(2); response = oauth.doRefreshTokenRequest(refreshTokenString, "password"); assertEquals(400, response.getStatusCode()); assertEquals("invalid_grant", response.getError()); @@ -970,6 +999,8 @@ public class RefreshTokenTest extends AbstractKeycloakTest { events.expectCodeToToken(codeId, sessionId).user(userId).assertEvent(); adminClient.realm("test").users().delete(userId); + + setTimeOffset(2); response = oauth.doRefreshTokenRequest(refreshTokenString, "password"); assertEquals(400, response.getStatusCode()); assertEquals("invalid_grant", response.getError());