diff --git a/docs/documentation/server_admin/topics/organizations/managing-identity-providers.adoc b/docs/documentation/server_admin/topics/organizations/managing-identity-providers.adoc
index 74c9483b164..f0fe3ee2f93 100644
--- a/docs/documentation/server_admin/topics/organizations/managing-identity-providers.adoc
+++ b/docs/documentation/server_admin/topics/organizations/managing-identity-providers.adoc
@@ -42,6 +42,9 @@ The domain from the organization that you want to link with the identity provide
Hide on login page::
If this identity provider should be hidden in login pages when the user is authenticating in the scope of the organization.
+Hide on login page when organization not resolved::
+If enabled, the identity provider will be hidden on the login page when the organization cannot be resolved based on the user's email domain. Otherwise, the identity provider will be shown on the login page regardless of whether the organization is resolved or not. If 'Hide on login page' is also enabled, the identity provider will always be hidden on the login page.
+
Redirect when email domain matches::
If members should be automatically redirected to the identity provider when their email domain matches the domain set to the identity provider. If the domain is set to `Any`, members whose email domain matches *any* of the organization domains will be redirected to the identity provider.
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 4b4a7d71957..75acb8b1178 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
@@ -3710,4 +3710,6 @@ smtpSocketWriteTimeoutHelp=The timeout in milliseconds for writing to the SMTP s
eventTypes.USER_SESSION_DELETED.name=User session deleted
eventTypes.USER_SESSION_DELETED.description=User session deleted
eventTypes.USER_SESSION_DELETED_ERROR.name=User session deleted error
-eventTypes.USER_SESSION_DELETED_ERROR.description=User session deleted error
\ No newline at end of file
+eventTypes.USER_SESSION_DELETED_ERROR.description=User session deleted error
+hideOnLoginWhenOrgNotResolved=Hide on login page when organization not resolved
+hideOnLoginWhenOrgNotResolvedHelp=If enabled, the identity provider will be hidden on the login page when the organization cannot be resolved based on the user's email domain. Otherwise, the identity provider will be shown on the login page regardless of whether the organization is resolved or not. If 'Hide on login page' is also enabled, the identity provider will always be hidden on the login page.
\ 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 4697e122874..bc64f2e7b33 100644
--- a/js/apps/admin-ui/src/organizations/LinkIdentityProviderModal.tsx
+++ b/js/apps/admin-ui/src/organizations/LinkIdentityProviderModal.tsx
@@ -153,6 +153,14 @@ export const LinkIdentityProviderModal = ({
labelIcon={t("hideOnLoginPageHelp")}
defaultValue={true}
/>
+
createIdentityProvider(this.realm, this.baseURI, idp))
.sorted(IDP_COMPARATOR_INSTANCE).toList();
}
+ Predicate defaultFilter = idp -> {
+ if (idp.isEnabled() && !Objects.equals(existingIDP, idp.getAlias())) {
+ if (organization == null) {
+ Map config = idp.getConfig();
+ return !Boolean.parseBoolean(config.get(OrganizationModel.HIDE_IDP_ON_LOGIN_WHEN_ORGANIZATION_UNKNOWN));
+ }
+
+ return true;
+ }
+
+ return false;
+ };
if (onlyOrganizationBrokers) {
// we already have the organization, just fetch the organization's public enabled IDPs.
if (this.organization != null) {
@@ -72,12 +85,12 @@ public class OrganizationAwareIdentityProviderBean extends IdentityProviderBean
}
// we don't have a specific organization - fetch public enabled IDPs linked to any org.
return session.identityProviders().getForLogin(ORG_ONLY, null)
- .filter(idp -> idp.isEnabled() && !Objects.equals(existingIDP, idp.getAlias())) // re-check isEnabled as idp might have been wrapped.
+ .filter(defaultFilter) // re-check isEnabled as idp might have been wrapped.
.map(idp -> createIdentityProvider(this.realm, this.baseURI, idp))
.sorted(IDP_COMPARATOR_INSTANCE).toList();
}
return session.identityProviders().getForLogin(ALL, this.organization != null ? this.organization.getId() : null)
- .filter(idp -> idp.isEnabled() && !Objects.equals(existingIDP, idp.getAlias())) // re-check isEnabled as idp might have been wrapped.
+ .filter(defaultFilter) // re-check isEnabled as idp might have been wrapped.
.map(idp -> createIdentityProvider(this.realm, this.baseURI, idp))
.sorted(IDP_COMPARATOR_INSTANCE).toList();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/broker/AbstractBrokerSelfRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/broker/AbstractBrokerSelfRegistrationTest.java
index 36d7bc471fa..4a8bd547faf 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/broker/AbstractBrokerSelfRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/broker/AbstractBrokerSelfRegistrationTest.java
@@ -776,6 +776,48 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
Assert.assertFalse(loginPage.isSocialButtonPresent(org0Broker.getAlias()));
}
+ @Test
+ public void testDoNotShowBrokersIfOrganizationNotResolved() {
+ String org0Name = "org-0";
+ OrganizationResource org0 = testRealm().organizations().get(createOrganization(org0Name).getId());
+ IdentityProviderRepresentation org0Broker = org0.identityProviders().getIdentityProviders().get(0);
+ org0Broker.setHideOnLogin(false);
+ org0Broker.getConfig().put(OrganizationModel.HIDE_IDP_ON_LOGIN_WHEN_ORGANIZATION_UNKNOWN, Boolean.TRUE.toString());
+ org0Broker.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
+ testRealm().identityProviders().get(org0Broker.getAlias()).update(org0Broker);
+
+ // do not show if organization cannot be resolved
+ oauth.clientId("broker-app");
+ loginPage.open(bc.consumerRealmName());
+ loginPage.loginUsername("user@unknowndomain.org");
+ Assert.assertFalse(loginPage.isSocialButtonPresent(org0Broker.getAlias()));
+
+ // show if organization can be resolved
+ loginPage.open(bc.consumerRealmName());
+ loginPage.loginUsername("user@org-0.org");
+ Assert.assertTrue(loginPage.isSocialButtonPresent(org0Broker.getAlias()));
+
+ // show if the config is set to false
+ org0Broker.getConfig().put(OrganizationModel.HIDE_IDP_ON_LOGIN_WHEN_ORGANIZATION_UNKNOWN, Boolean.FALSE.toString());
+ testRealm().identityProviders().get(org0Broker.getAlias()).update(org0Broker);
+ loginPage.open(bc.consumerRealmName());
+ loginPage.loginUsername("user@unknowndomain.org");
+ Assert.assertTrue(loginPage.isSocialButtonPresent(org0Broker.getAlias()));
+ loginPage.open(bc.consumerRealmName());
+ loginPage.loginUsername("user@org-0.org");
+ Assert.assertTrue(loginPage.isSocialButtonPresent(org0Broker.getAlias()));
+
+ // hide if hide on login is set to true
+ org0Broker.setHideOnLogin(true);
+ testRealm().identityProviders().get(org0Broker.getAlias()).update(org0Broker);
+ loginPage.open(bc.consumerRealmName());
+ loginPage.loginUsername("user@unknowndomain.org");
+ Assert.assertFalse(loginPage.isSocialButtonPresent(org0Broker.getAlias()));
+ loginPage.open(bc.consumerRealmName());
+ loginPage.loginUsername("user@org-0.org");
+ Assert.assertFalse(loginPage.isSocialButtonPresent(org0Broker.getAlias()));
+ }
+
@Test
public void testLoginUsingBrokerWithoutDomain() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());