Always include offline_access scope when refreshing with offline token
Closes #27878 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
parent
99478887a4
commit
939420cea1
2 changed files with 37 additions and 15 deletions
|
@ -414,7 +414,7 @@ public class TokenManager {
|
||||||
//if scope parameter is not null, remove every scope that is not part of scope parameter
|
//if scope parameter is not null, remove every scope that is not part of scope parameter
|
||||||
if (scopeParameter != null && ! scopeParameter.isEmpty()) {
|
if (scopeParameter != null && ! scopeParameter.isEmpty()) {
|
||||||
Set<String> scopeParamScopes = Arrays.stream(scopeParameter.split(" ")).collect(Collectors.toSet());
|
Set<String> scopeParamScopes = Arrays.stream(scopeParameter.split(" ")).collect(Collectors.toSet());
|
||||||
oldTokenScope = Arrays.stream(oldTokenScope.split(" ")).filter(sc -> scopeParamScopes.contains(sc))
|
oldTokenScope = Arrays.stream(oldTokenScope.split(" ")).filter(sc -> scopeParamScopes.contains(sc) || sc.equals(OAuth2Constants.OFFLINE_ACCESS))
|
||||||
.collect(Collectors.joining(" "));
|
.collect(Collectors.joining(" "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -260,7 +260,7 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
assertTrue(tokenResponse.getScope().contains(OAuth2Constants.OFFLINE_ACCESS));
|
assertTrue(tokenResponse.getScope().contains(OAuth2Constants.OFFLINE_ACCESS));
|
||||||
|
|
||||||
String newRefreshTokenString = testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, sessionId, userId, false);
|
String newRefreshTokenString = testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, sessionId, userId);
|
||||||
|
|
||||||
// Change offset to very big value to ensure offline session expires
|
// Change offset to very big value to ensure offline session expires
|
||||||
setTimeOffset(3000000);
|
setTimeOffset(3000000);
|
||||||
|
@ -281,7 +281,7 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String testRefreshWithOfflineToken(AccessToken oldToken, RefreshToken offlineToken, String offlineTokenString,
|
private String testRefreshWithOfflineToken(AccessToken oldToken, RefreshToken offlineToken, String offlineTokenString,
|
||||||
final String sessionId, String userId, boolean scopeParameterExist) {
|
final String sessionId, String userId) {
|
||||||
// Change offset to big value to ensure userSession expired
|
// Change offset to big value to ensure userSession expired
|
||||||
setTimeOffset(99999);
|
setTimeOffset(99999);
|
||||||
assertFalse(oldToken.isActive());
|
assertFalse(oldToken.isActive());
|
||||||
|
@ -306,7 +306,7 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
Assert.assertNotEquals(oldToken.getId(), refreshedToken.getId());
|
Assert.assertNotEquals(oldToken.getId(), refreshedToken.getId());
|
||||||
|
|
||||||
// scope parameter contains "offline_access" if not filter via scope parameter
|
// scope parameter contains "offline_access" if not filter via scope parameter
|
||||||
assertTrue(scopeParameterExist ? !refreshedToken.getScope().contains(OAuth2Constants.OFFLINE_ACCESS) : refreshedToken.getScope().contains(OAuth2Constants.OFFLINE_ACCESS));
|
assertTrue(refreshedToken.getScope().contains(OAuth2Constants.OFFLINE_ACCESS));
|
||||||
// Assert refresh token scope parameter contains "offline_access"
|
// Assert refresh token scope parameter contains "offline_access"
|
||||||
assertTrue(newRefreshTokenFull.getScope().contains(OAuth2Constants.OFFLINE_ACCESS));
|
assertTrue(newRefreshTokenFull.getScope().contains(OAuth2Constants.OFFLINE_ACCESS));
|
||||||
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, newRefreshTokenFull.getType());
|
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, newRefreshTokenFull.getType());
|
||||||
|
@ -358,10 +358,10 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
||||||
Assert.assertEquals(0, offlineToken.getExpiration());
|
Assert.assertEquals(0, offlineToken.getExpiration());
|
||||||
|
|
||||||
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId, false);
|
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId);
|
||||||
|
|
||||||
// Assert same token can be refreshed again
|
// Assert same token can be refreshed again
|
||||||
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId, false);
|
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -393,7 +393,7 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
||||||
Assert.assertEquals(0, offlineToken.getExpiration());
|
Assert.assertEquals(0, offlineToken.getExpiration());
|
||||||
|
|
||||||
String offlineTokenString2 = testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId, false);
|
String offlineTokenString2 = testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId);
|
||||||
RefreshToken offlineToken2 = oauth.parseRefreshToken(offlineTokenString2);
|
RefreshToken offlineToken2 = oauth.parseRefreshToken(offlineTokenString2);
|
||||||
|
|
||||||
// Assert second refresh with same refresh token will fail
|
// Assert second refresh with same refresh token will fail
|
||||||
|
@ -442,7 +442,7 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
||||||
Assert.assertEquals(0, offlineToken.getExpiration());
|
Assert.assertEquals(0, offlineToken.getExpiration());
|
||||||
|
|
||||||
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, false);
|
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId);
|
||||||
|
|
||||||
// Now retrieve another offline token and verify that previous offline token is still valid
|
// Now retrieve another offline token and verify that previous offline token is still valid
|
||||||
tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
|
tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
|
||||||
|
@ -462,8 +462,8 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
|
|
||||||
// Refresh with both offline tokens is fine
|
// Refresh with both offline tokens is fine
|
||||||
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, false);
|
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId);
|
||||||
testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId, false);
|
testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -978,7 +978,7 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
||||||
Assert.assertEquals(0, offlineToken.getExpiration());
|
Assert.assertEquals(0, offlineToken.getExpiration());
|
||||||
|
|
||||||
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, false);
|
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId);
|
||||||
|
|
||||||
// Now retrieve another offline token and decode that previous offline token is still valid
|
// Now retrieve another offline token and decode that previous offline token is still valid
|
||||||
tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
|
tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
|
||||||
|
@ -998,8 +998,8 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
|
|
||||||
// Refresh with both offline tokens is fine
|
// Refresh with both offline tokens is fine
|
||||||
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, false);
|
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId);
|
||||||
testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId, false);
|
testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1050,9 +1050,9 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
|
||||||
Assert.assertEquals(0, offlineToken.getExpiration());
|
Assert.assertEquals(0, offlineToken.getExpiration());
|
||||||
|
|
||||||
//refresh token without sending offline_access scope => access token without it and same refresh token
|
//refresh token without sending offline_access scope
|
||||||
oauth.scope("phone");
|
oauth.scope("phone");
|
||||||
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, true);
|
testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1289,4 +1289,26 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
||||||
client.update(clientRepresentation);
|
client.update(clientRepresentation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void offlineTokenRefreshWithoutOfflineAccessScope() {
|
||||||
|
ClientManager.realm(adminClient.realm("test")).clientId("offline-client").fullScopeAllowed(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
oauth.scope("openid " + OAuth2Constants.OFFLINE_ACCESS);
|
||||||
|
oauth.clientId("offline-client");
|
||||||
|
oauth.redirectUri(offlineClientAppUri);
|
||||||
|
oauth.doLogin("test-user@localhost", "password");
|
||||||
|
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||||
|
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "secret1");
|
||||||
|
|
||||||
|
oauth.scope("openid");
|
||||||
|
response = oauth.doRefreshTokenRequest(response.getRefreshToken(), "secret1");
|
||||||
|
assertEquals(200, response.getStatusCode());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ClientManager.realm(adminClient.realm("test")).clientId("offline-client").fullScopeAllowed(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue