diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties
index 67be1d9980..e617e15ec0 100644
--- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties
+++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties
@@ -3191,4 +3191,6 @@ linkUpdatedSuccessful=Identity provider link successfully updated
linkUpdateError=Could not update link to identity provider\: {{error}}
noResultsFound=No results found
linkedOrganization=Linked organization
-send=Send
\ No newline at end of file
+send=Send
+redirectWhenEmailMatches=Redirect when email domain matches
+redirectWhenEmailMatchesHelp=Automatically redirect the user to this identity provider when the email domain matches the domain
\ No newline at end of file
diff --git a/js/apps/admin-ui/src/organizations/LinkIdentityProviderModal.tsx b/js/apps/admin-ui/src/organizations/LinkIdentityProviderModal.tsx
index 22531614d5..44c0f6f594 100644
--- a/js/apps/admin-ui/src/organizations/LinkIdentityProviderModal.tsx
+++ b/js/apps/admin-ui/src/organizations/LinkIdentityProviderModal.tsx
@@ -145,6 +145,14 @@ export const LinkIdentityProviderModal = ({
labelIcon={t("shownOnLoginPageHelp")}
stringify
/>
+
diff --git a/server-spi/src/main/java/org/keycloak/models/OrganizationModel.java b/server-spi/src/main/java/org/keycloak/models/OrganizationModel.java
index 88a689843a..baa8a5315b 100644
--- a/server-spi/src/main/java/org/keycloak/models/OrganizationModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/OrganizationModel.java
@@ -29,6 +29,24 @@ public interface OrganizationModel {
String ORGANIZATION_DOMAIN_ATTRIBUTE = "kc.org.domain";
String BROKER_PUBLIC = "kc.org.broker.public";
+ enum IdentityProviderRedirectMode {
+ EMAIL_MATCH("kc.org.broker.redirect.mode.email-matches");
+
+ private final String key;
+
+ IdentityProviderRedirectMode(String key) {
+ this.key = key;
+ }
+
+ public boolean isSet(IdentityProviderModel broker) {
+ return Boolean.parseBoolean(broker.getConfig().get(key));
+ }
+
+ public String getKey() {
+ return key;
+ }
+ }
+
String getId();
void setName(String name);
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 4045ddacd6..a5b174a73b 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
@@ -32,6 +32,7 @@ import org.keycloak.http.HttpRequest;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
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.utils.FormMessage;
@@ -109,14 +110,8 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator {
List brokers = organization.getIdentityProviders().toList();
- for (IdentityProviderModel broker : brokers) {
- String idpDomain = broker.getConfig().get(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
-
- if (emailDomain.equals(idpDomain)) {
- // redirect the user using the broker that matches the email domain
- redirect(context, broker.getAlias(), username);
- return;
- }
+ if (redirect(context, brokers, username, emailDomain)) {
+ return;
}
if (!hasPublicBrokers(brokers)) {
@@ -195,4 +190,20 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator {
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return realm.isOrganizationsEnabled();
}
+
+ protected boolean redirect(AuthenticationFlowContext context, List brokers, String username, String emailDomain) {
+ for (IdentityProviderModel broker : brokers) {
+ if (IdentityProviderRedirectMode.EMAIL_MATCH.isSet(broker)) {
+ String idpDomain = broker.getConfig().get(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
+
+ if (emailDomain.equals(idpDomain)) {
+ // redirect the user using the broker that matches the email domain
+ redirect(context, broker.getAlias(), username);
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/AbstractBrokerSelfRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/AbstractBrokerSelfRegistrationTest.java
index ed54987275..5d9d9b6224 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/AbstractBrokerSelfRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/AbstractBrokerSelfRegistrationTest.java
@@ -34,6 +34,7 @@ import org.keycloak.admin.client.resource.OrganizationResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.OrganizationModel;
+import org.keycloak.models.OrganizationModel.IdentityProviderRedirectMode;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.idm.ErrorRepresentation;
@@ -498,6 +499,32 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
assertEquals(bc.getIDPAlias(), federatedIdentities.get(0).getIdentityProvider());
}
+ @Test
+ public void testDoNotRedirectToIdentityProviderAssociatedWithOrganizationDomain() {
+ OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
+ IdentityProviderRepresentation idp = organization.identityProviders().get(bc.getIDPAlias()).toRepresentation();
+ idp.getConfig().put(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE, "neworg.org");
+ idp.getConfig().put(IdentityProviderRedirectMode.EMAIL_MATCH.getKey(), Boolean.FALSE.toString());
+ idp.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
+ testRealm().identityProviders().get(bc.getIDPAlias()).update(idp);
+
+ oauth.clientId("broker-app");
+ loginPage.open(bc.consumerRealmName());
+ log.debug("Logging in");
+ Assert.assertFalse(loginPage.isPasswordInputPresent());
+ Assert.assertFalse(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
+ Assert.assertFalse(loginPage.isSocialButtonPresent(idp.getAlias()));
+ loginPage.loginUsername(bc.getUserEmail());
+
+ // user not automatically redirected to the organization identity provider
+ waitForPage(driver, "sign in to", true);
+ Assert.assertTrue("Driver should be on the consumer realm page right now",
+ driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
+ Assert.assertFalse(loginPage.isPasswordInputPresent());
+ Assert.assertTrue(driver.getPageSource().contains("Your email domain matches the " + organizationName + " organization but you don't have an account yet."));
+ Assert.assertTrue(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
+ }
+
@Test
public void testLoginUsingBrokerWithoutDomain() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/AbstractOrganizationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/AbstractOrganizationTest.java
index 52be10cfbd..628049026a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/AbstractOrganizationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/AbstractOrganizationTest.java
@@ -32,6 +32,7 @@ import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.admin.client.resource.OrganizationResource;
import org.keycloak.models.OrganizationModel;
import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.models.OrganizationModel.IdentityProviderRedirectMode;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.GroupRepresentation;
@@ -117,6 +118,7 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
}
// set the idp domain to the first domain used to create the org.
broker.getConfig().put(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE, orgDomains[0]);
+ broker.getConfig().put(IdentityProviderRedirectMode.EMAIL_MATCH.getKey(), Boolean.TRUE.toString());
testRealm.identityProviders().create(broker).close();
testCleanup.addCleanup(testRealm.identityProviders().get(broker.getAlias())::remove);
testRealm.organizations().get(id).identityProviders().addIdentityProvider(broker.getAlias()).close();
@@ -201,7 +203,7 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
assertFalse(driver.getPageSource().contains("kc.org"));
updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), email, "Firstname", "Lastname");
assertThat(appPage.getRequestType(),is(AppPage.RequestType.AUTH_RESPONSE));
-
+
assertIsMember(email, organization);
}