Token revocation may not correctly revoke related access tokens

closes #35813

Signed-off-by: mposolda <mposolda@gmail.com>
(cherry picked from commit efdc42c2a4132b9c8c953c2ed0bea9d71dcb848e)
This commit is contained in:
mposolda 2024-12-11 14:34:03 +01:00 committed by Alexander Schwartz
parent 9f5b60cdc3
commit c5bed0a940
2 changed files with 30 additions and 10 deletions

View File

@ -252,16 +252,16 @@ public class TokenRevocationEndpoint {
if (userSession != null) {
new UserSessionManager(session).removeClientFromOfflineUserSession(realm, userSession, client, user);
}
} else {
UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionId());
if (userSession != null) {
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (clientSession != null) {
TokenManager.dettachClientSession(clientSession);
// TODO: Might need optimization to prevent loading client sessions from cache in getAuthenticatedClientSessions()
if (userSession.getAuthenticatedClientSessions().isEmpty()) {
session.sessions().removeUserSession(realm, userSession);
}
}
// Always remove "online" session as well if exists to make sure that issued access-tokens are revoked as well
UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionId());
if (userSession != null) {
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (clientSession != null) {
TokenManager.dettachClientSession(clientSession);
// TODO: Might need optimization to prevent loading client sessions from cache in getAuthenticatedClientSessions()
if (userSession.getAuthenticatedClientSessions().isEmpty()) {
session.sessions().removeUserSession(realm, userSession);
}
}
}

View File

@ -211,6 +211,26 @@ public class TokenRevocationTest extends AbstractKeycloakTest {
isTokenDisabled(tokenResponse, "test-app");
}
@Test
public void testRevokeOfflineTokenWithOnlineSSOSession() throws Exception {
OAuthClient.AccessTokenResponse tokenResponse1 = login("test-app", "test-user@localhost", "password");
// Offline login of same client in same SSO session as previous login
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
OAuthClient.AccessTokenResponse tokenResponse2 = login("test-app", "test-user@localhost", "password");
// Session IDs of "offline" and online session are same for now. This may change in the future
Assert.assertEquals(tokenResponse1.getSessionState(), tokenResponse2.getSessionState());
isTokenEnabled(tokenResponse2, "test-app");
// Disable both offline and refresh
CloseableHttpResponse response = oauth.doTokenRevoke(tokenResponse2.getRefreshToken(), "refresh_token", "password");
assertThat(response, Matchers.statusCodeIsHC(Status.OK));
isTokenDisabled(tokenResponse2, "test-app");
}
@Test
public void testTokenTypeHint() throws Exception {
// different token_type_hint