mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Resolve first the user by username and fallback to the email during the identity-first login flow
Closes #38852 Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
cf6c8b07c5
commit
b9d38d0fe9
@ -19,6 +19,7 @@ package org.keycloak.organization.authentication.authenticators.browser;
|
||||
|
||||
import static org.keycloak.authentication.AuthenticatorUtil.isSSOAuthentication;
|
||||
import static org.keycloak.models.OrganizationDomainModel.ANY_DOMAIN;
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.findUserByNameOrEmail;
|
||||
import static org.keycloak.organization.utils.Organizations.getEmailDomain;
|
||||
import static org.keycloak.organization.utils.Organizations.isEnabledAndOrganizationsPresent;
|
||||
import static org.keycloak.organization.utils.Organizations.resolveHomeBroker;
|
||||
@ -33,7 +34,6 @@ import java.util.stream.Stream;
|
||||
import jakarta.ws.rs.core.MultivaluedHashMap;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.AuthenticationFlowError;
|
||||
import org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticator;
|
||||
@ -51,8 +51,8 @@ import org.keycloak.models.OrganizationModel;
|
||||
import org.keycloak.models.OrganizationModel.IdentityProviderRedirectMode;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.organization.OrganizationProvider;
|
||||
import org.keycloak.organization.forms.login.freemarker.model.OrganizationAwareAuthenticationContextBean;
|
||||
import org.keycloak.organization.forms.login.freemarker.model.OrganizationAwareIdentityProviderBean;
|
||||
@ -280,9 +280,8 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator {
|
||||
return null;
|
||||
}
|
||||
|
||||
UserProvider users = session.users();
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
UserModel user = Optional.ofNullable(users.getUserByEmail(realm, username)).orElseGet(() -> users.getUserByUsername(realm, username));
|
||||
UserModel user = findUserByNameOrEmail(session, realm, username);
|
||||
|
||||
// make sure the organization will be resolved based on the username provided
|
||||
clearAuthenticationSession(context);
|
||||
|
||||
@ -26,6 +26,7 @@ import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
@ -35,12 +36,15 @@ import org.keycloak.models.UserModel.RequiredAction;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.organization.authentication.authenticators.browser.OrganizationAuthenticatorFactory;
|
||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.organization.admin.AbstractOrganizationTest;
|
||||
import org.keycloak.testsuite.runonserver.RunOnServer;
|
||||
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
|
||||
import org.keycloak.testsuite.util.FlowUtil;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
public class OrganizationAuthenticationTest extends AbstractOrganizationTest {
|
||||
|
||||
@ -209,6 +213,61 @@ public class OrganizationAuthenticationTest extends AbstractOrganizationTest {
|
||||
appPage.assertCurrent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateEmailsEnabled() {
|
||||
RealmRepresentation realm = testRealm().toRepresentation();
|
||||
|
||||
realm.setDuplicateEmailsAllowed(true);
|
||||
realm.setLoginWithEmailAllowed(false);
|
||||
realm.setRegistrationEmailAsUsername(false);
|
||||
|
||||
testRealm().update(realm);
|
||||
|
||||
OrganizationRepresentation organization = createOrganization();
|
||||
OrganizationResource organizationResource = testRealm().organizations().get(organization.getId());
|
||||
UserRepresentation member = addMember(organizationResource);
|
||||
UserRepresentation duplicatedUser = UserBuilder.create()
|
||||
.username("duplicated-user")
|
||||
.password("duplicated-user")
|
||||
.email(member.getEmail())
|
||||
.enabled(true).build();
|
||||
try (Response response = testRealm().users().create(duplicatedUser)) {
|
||||
duplicatedUser.setId(ApiUtil.getCreatedId(response));
|
||||
}
|
||||
|
||||
// user with a unique username can authenticate to his account using a unique username
|
||||
oauth.clientId("broker-app");
|
||||
oauth.realm(bc.consumerRealmName());
|
||||
oauth.loginForm().open();
|
||||
loginPage.loginUsername(member.getUsername());
|
||||
loginPage.clickSignIn();
|
||||
loginPage.login(memberPassword);
|
||||
appPage.assertCurrent();
|
||||
testRealm().users().get(member.getId()).logout();
|
||||
|
||||
// a different account with the same email can also authenticate using a unique username
|
||||
oauth.loginForm().open();
|
||||
loginPage.loginUsername(duplicatedUser.getUsername());
|
||||
loginPage.clickSignIn();
|
||||
loginPage.login(duplicatedUser.getUsername());
|
||||
appPage.assertCurrent();
|
||||
testRealm().users().get(duplicatedUser.getId()).logout();
|
||||
|
||||
// trying to authenticate with the duplicated user using the email will fail because the username is the email of a different account
|
||||
oauth.loginForm().open();
|
||||
loginPage.loginUsername(duplicatedUser.getEmail());
|
||||
loginPage.clickSignIn();
|
||||
loginPage.login(duplicatedUser.getEmail());
|
||||
assertThat(loginPage.getInputError(), is("Invalid password."));
|
||||
|
||||
// trying to authenticate to the account that has the email as username is ok
|
||||
oauth.loginForm().open();
|
||||
loginPage.loginUsername(member.getEmail());
|
||||
loginPage.clickSignIn();
|
||||
loginPage.login(memberPassword);
|
||||
appPage.assertCurrent();
|
||||
}
|
||||
|
||||
private void runOnServer(RunOnServer function) {
|
||||
testingClient.server(bc.consumerRealmName()).run(function);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user