From 04d6a6f6ac905e9611d581b5abd4c990f6e90e81 Mon Sep 17 00:00:00 2001 From: Olivier Boudet Date: Sun, 4 May 2025 22:45:50 +0200 Subject: [PATCH] feat: organization authenticator : bypass username form when login_hint is set Signed-off-by: Olivier Boudet --- .../browser/OrganizationAuthenticator.java | 13 +++++++++++++ .../OrganizationAuthenticationTest.java | 3 +-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/services/src/main/java/org/keycloak/organization/authentication/authenticators/browser/OrganizationAuthenticator.java b/services/src/main/java/org/keycloak/organization/authentication/authenticators/browser/OrganizationAuthenticator.java index 62ada1bf9b00..ca31613619a2 100644 --- a/services/src/main/java/org/keycloak/organization/authentication/authenticators/browser/OrganizationAuthenticator.java +++ b/services/src/main/java/org/keycloak/organization/authentication/authenticators/browser/OrganizationAuthenticator.java @@ -23,6 +23,7 @@ import static org.keycloak.organization.utils.Organizations.getEmailDomain; import static org.keycloak.organization.utils.Organizations.isEnabledAndOrganizationsPresent; import static org.keycloak.organization.utils.Organizations.resolveHomeBroker; +import static org.keycloak.utils.StringUtil.isNotBlank; import java.util.List; import java.util.Map; @@ -64,6 +65,8 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator { + private static final String LOGIN_HINT_ALREADY_HANDLED = "loginHintAlreadyHandled"; + private final KeycloakSession session; public OrganizationAuthenticator(KeycloakSession session) { @@ -79,6 +82,16 @@ public void authenticate(AuthenticationFlowContext context) { return; } + String loginHint = session.getContext().getAuthenticationSession().getClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM); + + if (isNotBlank(loginHint) && !"true".equals(context.getAuthenticationSession().getClientNote(LOGIN_HINT_ALREADY_HANDLED))) { + UserModel user = resolveUser(context, loginHint); + context.setUser(user); + + // set auth note to true to handle login_hint only once, we don't want to handle it again after a flow restart + context.getAuthenticationSession().setClientNote(LOGIN_HINT_ALREADY_HANDLED, "true"); + } + OrganizationModel organization = Organizations.resolveOrganization(session); if (organization == null) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/authentication/OrganizationAuthenticationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/authentication/OrganizationAuthenticationTest.java index afe3416fd02a..24426b528c2c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/authentication/OrganizationAuthenticationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/authentication/OrganizationAuthenticationTest.java @@ -205,10 +205,9 @@ public void testLoginHint() { String expectedUsername = URLEncoder.encode(member.getEmail(), StandardCharsets.UTF_8); oauth.realm(bc.consumerRealmName()); oauth.loginForm().loginHint(expectedUsername).open(); - assertThat(loginPage.getUsername(), Matchers.equalTo(URLDecoder.decode(expectedUsername, StandardCharsets.UTF_8))); + assertThat(loginPage.getAttemptedUsername(), Matchers.equalTo(URLDecoder.decode(expectedUsername, StandardCharsets.UTF_8))); // continue authenticating without setting the username - loginPage.clickSignIn(); loginPage.login(memberPassword); appPage.assertCurrent(); }