From db44c8a38cb1fd85f2a24b854bb358464ed82a97 Mon Sep 17 00:00:00 2001 From: Oluwatobi Mustapha Date: Mon, 9 Mar 2026 15:41:33 +0100 Subject: [PATCH] Handle auth-session cleanup when client is missing or disabled (#46878) Fixes #46738 Signed-off-by: Oluwatobi Mustapha --- .../services/resources/SessionCodeChecks.java | 11 +++++-- .../keycloak/testsuite/forms/LoginTest.java | 30 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/services/src/main/java/org/keycloak/services/resources/SessionCodeChecks.java b/services/src/main/java/org/keycloak/services/resources/SessionCodeChecks.java index b99626aa638..8195c198749 100644 --- a/services/src/main/java/org/keycloak/services/resources/SessionCodeChecks.java +++ b/services/src/main/java/org/keycloak/services/resources/SessionCodeChecks.java @@ -261,7 +261,7 @@ public class SessionCodeChecks { event.error(Errors.CLIENT_NOT_FOUND); session.getProvider(LoginFormsProvider.class).setDetachedAuthSession(); response = ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, Messages.UNKNOWN_LOGIN_REQUESTER); - clientCode.removeExpiredClientSession(); + removeAuthenticationSession(authSession); return false; } @@ -272,7 +272,7 @@ public class SessionCodeChecks { event.error(Errors.CLIENT_DISABLED); session.getProvider(LoginFormsProvider.class).setDetachedAuthSession(); response = ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, Messages.LOGIN_REQUESTER_NOT_ENABLED); - clientCode.removeExpiredClientSession(); + removeAuthenticationSession(authSession); return false; } @@ -513,6 +513,13 @@ public class SessionCodeChecks { return event; } + private void removeAuthenticationSession(AuthenticationSessionModel authSession) { + ClientSessionCode codeToRemove = clientCode != null + ? clientCode + : new ClientSessionCode<>(session, realm, authSession); + codeToRemove.removeExpiredClientSession(); + } + protected boolean checkClientDisabled(ClientModel client) { return !client.isEnabled(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java index 9f5f6c90191..a3d02c63b2d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java @@ -909,6 +909,36 @@ public class LoginTest extends AbstractChangeImportedUserPasswordsTest { errorPage.assertCurrent(); } + @Test + public void loginWithClientDisabledInActiveAuthenticationSession() { + ClientResource clientResource = findClientByClientId(adminClient.realm("test"), "test-app"); + ClientRepresentation clientRepresentation = clientResource.toRepresentation(); + boolean wasEnabled = clientRepresentation.isEnabled(); + + try { + oauth.clientId("test-app"); + oauth.openLoginForm(); + loginPage.assertCurrent(); + + clientRepresentation.setEnabled(false); + clientResource.update(clientRepresentation); + + loginPage.login("test-user@localhost", getPassword("test-user@localhost")); + + errorPage.assertCurrent(); + assertEquals("Login requester not enabled", errorPage.getError()); + events.expect(EventType.LOGIN) + .client("test-app") + .user((String) null) + .session((String) null) + .error(Errors.CLIENT_DISABLED) + .assertEvent(); + } finally { + clientRepresentation.setEnabled(wasEnabled); + clientResource.update(clientRepresentation); + } + } + @Test public void openLoginFormWithDifferentApplication() throws Exception { oauth.clientId("root-url-client");