Remove authentication session during logout if no valid user session is found

Closes #39923

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2025-06-10 10:10:34 -03:00 committed by GitHub
parent f35282e02c
commit 2e09c31ac1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 64 additions and 0 deletions

View File

@ -39,6 +39,7 @@ import org.keycloak.locale.LocaleSelectorProvider;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
@ -413,6 +414,13 @@ public class LogoutEndpoint {
if (userSession == null) {
event.event(EventType.LOGOUT);
event.error(Errors.SESSION_EXPIRED);
KeycloakContext context = session.getContext();
AuthenticationSessionModel authSession = context.getAuthenticationSession();
if (authSession != null) {
// no valid session, make sure the current root auth session is also deleted and restart cookies
new AuthenticationSessionManager(session).removeAuthenticationSession(authSession.getRealm(), authSession, true);
}
} else {
Integer idTokenIssuedAt = Integer.parseInt(idTokenIssuedAtStr);
checkTokenIssuedAt(idTokenIssuedAt, userSession);

View File

@ -30,6 +30,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.util.Retry;
import org.keycloak.common.util.Time;
import org.keycloak.constants.AdapterConstants;
@ -56,9 +57,11 @@ import org.keycloak.testsuite.util.Matchers;
import org.keycloak.testsuite.util.ProtocolMapperUtil;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.TokenSignatureUtil;
import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
import org.keycloak.testsuite.util.oauth.LogoutResponse;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -178,6 +181,59 @@ public class LogoutTest extends AbstractKeycloakTest {
oauth.client("test-app", "password");
}
@Test
public void testRemoveAuthSessionWhenUserSessionFromIdTokenIsInvalid() throws IOException {
RealmResource realm = adminClient.realm("test");
for (int i = 0; i < 2; i++) {
realm.users().create(UserBuilder.create()
.username("user-0")
.password("password")
.email("user-0@keycloak")
.firstName("first")
.lastName("last")
.enabled(true)
.build()).close();
UserRepresentation user = ApiUtil.findUserByUsername(realm, "user-0");
Assert.assertNotNull(user);
loginPage.open();
loginPage.login("user-0", "password");
String code = oauth.parseLoginResponse().getCode();
AccessTokenResponse tokenResponse = oauth.accessTokenRequest(code).param(AdapterConstants.CLIENT_SESSION_STATE, "client-session").send();
String idTokenString = tokenResponse.getIdToken();
realm.users().get(user.getId()).remove();
oauth.logoutForm()
.withRedirect()
.idTokenHint(idTokenString)
.postLogoutRedirectUri(oauth.APP_AUTH_ROOT)
.open();
realm.users().create(UserBuilder.create()
.username("user-1")
.password("password")
.email("user-1@keycloak")
.firstName("first")
.lastName("last")
.enabled(true)
.build()).close();
loginPage.open();
loginPage.login("user-1", "password");
code = oauth.parseLoginResponse().getCode();
tokenResponse = oauth.accessTokenRequest(code).param(AdapterConstants.CLIENT_SESSION_STATE, "client-session").send();
idTokenString = tokenResponse.getIdToken();
oauth.logoutForm()
.idTokenHint(idTokenString)
.postLogoutRedirectUri(oauth.APP_AUTH_ROOT)
.open();
user = ApiUtil.findUserByUsername(realm, "user-1");
Assert.assertNotNull(user);
realm.users().get(user.getId()).remove();
}
}
@Test
public void logoutUserByAdmin() {
loginPage.open();