KEYCLOAK-11838: Fixed unstable RefreshTokenTest (#6455)
This commit is contained in:
parent
28b41623eb
commit
bf8184221a
1 changed files with 110 additions and 79 deletions
|
@ -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());
|
||||
|
|
Loading…
Reference in a new issue