From e41553bcfb943f6658f1928e52dd96b4fa1420bf Mon Sep 17 00:00:00 2001 From: rmartinc Date: Wed, 23 Oct 2024 16:00:16 +0200 Subject: [PATCH] Create a new logout session when initiating it for another client Closes #34207 Signed-off-by: rmartinc --- .../oidc/endpoints/LogoutEndpoint.java | 2 +- .../managers/AuthenticationManager.java | 27 +++++++++++++------ .../oauth/RPInitiatedLogoutTest.java | 8 +++++- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java index b0eadc6649..2b476f1dcc 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java @@ -231,7 +231,7 @@ public class LogoutEndpoint { } } - AuthenticationSessionModel logoutSession = AuthenticationManager.createOrJoinLogoutSession(session, realm, new AuthenticationSessionManager(session), null, true); + AuthenticationSessionModel logoutSession = AuthenticationManager.createOrJoinLogoutSession(session, realm, new AuthenticationSessionManager(session), null, true, true); session.getContext().setAuthenticationSession(logoutSession); if (uiLocales != null) { logoutSession.setClientNote(LocaleSelectorProvider.CLIENT_REQUEST_LOCALE, uiLocales); diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index b996c2a586..67b8110d91 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -306,7 +306,7 @@ public class AuthenticationManager { final AuthenticationSessionManager asm = new AuthenticationSessionManager(session); AuthenticationSessionModel logoutAuthSession = - createOrJoinLogoutSession(session, realm, asm, userSession, false); + createOrJoinLogoutSession(session, realm, asm, userSession, false, false); boolean userSessionOnlyHasLoggedOutClients = false; try { @@ -341,7 +341,8 @@ public class AuthenticationManager { return backchannelLogoutResponse; } - public static AuthenticationSessionModel createOrJoinLogoutSession(KeycloakSession session, RealmModel realm, final AuthenticationSessionManager asm, UserSessionModel userSession, boolean browserCookie) { + public static AuthenticationSessionModel createOrJoinLogoutSession(KeycloakSession session, RealmModel realm, + final AuthenticationSessionManager asm, UserSessionModel userSession, boolean browserCookie, boolean initiateLogout) { AuthenticationSessionModel logoutSession = session.getContext().getAuthenticationSession(); if (logoutSession != null && AuthenticationSessionModel.Action.LOGGING_OUT.name().equals(logoutSession.getAction())) { return logoutSession; @@ -384,14 +385,24 @@ public class AuthenticationManager { .filter( authSession -> AuthenticationSessionModel.Action.LOGGING_OUT.name().equals(authSession.getAction())) .findFirst(); - AuthenticationSessionModel logoutAuthSession; + AuthenticationSessionModel logoutAuthSession = null, prevAuthSession = null; if (found.isPresent()) { - logoutAuthSession = found.get(); - logger.tracef("Found existing logout session for client '%s'. Authentication session id: %s", client.getClientId(), rootLogoutSession.getId()); - } else { + prevAuthSession = found.get(); + if (!initiateLogout || client.getId().equals(prevAuthSession.getClient().getId())) { + logoutAuthSession = prevAuthSession; + logger.tracef("Found existing logout session for client '%s'. Authentication session id: %s", client.getClientId(), rootLogoutSession.getId()); + } + } + + if (logoutAuthSession == null) { logoutAuthSession = rootLogoutSession.createAuthenticationSession(client); logoutAuthSession.setAction(AuthenticationSessionModel.Action.LOGGING_OUT.name()); logger.tracef("Creating logout session for client '%s'. Authentication session id: %s", client.getClientId(), rootLogoutSession.getId()); + if (prevAuthSession != null) { + // remove previous logout session for the other client + rootLogoutSession.removeAuthenticationSessionByTabId(prevAuthSession.getTabId()); + logger.tracef("Removing previous logout session for client '%s' in %s", prevAuthSession.getClient().getClientId(), rootLogoutSession.getId()); + } } session.getContext().setAuthenticationSession(logoutAuthSession); session.getContext().setClient(client); @@ -658,7 +669,7 @@ public class AuthenticationManager { } final AuthenticationSessionManager asm = new AuthenticationSessionManager(session); - AuthenticationSessionModel logoutAuthSession = createOrJoinLogoutSession(session, realm, asm, userSession, true); + AuthenticationSessionModel logoutAuthSession = createOrJoinLogoutSession(session, realm, asm, userSession, true, false); String brokerId = userSession.getNote(Details.IDENTITY_PROVIDER); String initiatingIdp = logoutAuthSession.getAuthNote(AuthenticationManager.LOGOUT_INITIATING_IDP); @@ -696,7 +707,7 @@ public class AuthenticationManager { public static Response finishBrowserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) { final AuthenticationSessionManager asm = new AuthenticationSessionManager(session); - AuthenticationSessionModel logoutAuthSession = createOrJoinLogoutSession(session, realm, asm, userSession, true); + AuthenticationSessionModel logoutAuthSession = createOrJoinLogoutSession(session, realm, asm, userSession, true, false); Response response = browserLogoutAllClients(userSession, session, realm, headers, uriInfo, logoutAuthSession); if (response != null) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java index fdc54426a9..d03f9cb826 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java @@ -684,7 +684,13 @@ public class RPInitiatedLogoutTest extends AbstractTestRealmKeycloakTest { public void logoutWithClientIdAndWithoutIdTokenHint() { OAuthClient.AccessTokenResponse tokenResponse = loginUser(); - String logoutUrl = oauth.getLogoutUrl().postLogoutRedirectUri(APP_REDIRECT_URI).clientId("test-app").state("somethingg").build(); + // logout url with no parameters, client is the account app + String logoutUrl = oauth.getLogoutUrl().build(); + driver.navigate().to(logoutUrl); + logoutConfirmPage.assertCurrent(); + + // change logout to our app with redirect uri + logoutUrl = oauth.getLogoutUrl().postLogoutRedirectUri(APP_REDIRECT_URI).clientId("test-app").state("somethingg").build(); driver.navigate().to(logoutUrl); // Assert logout confirmation page as id_token_hint was not sent. Session still exists. Assert default language on logout page (English)