diff --git a/CHANGELOG.md b/CHANGELOG.md index 51dab34c1e48..76b2d9152567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ Our Keycloak version is working well with PostgreSQL database. For using other S ### Added - Logo uri for IdPs +- Add cookie for chosen login IdPs + +### Fixed +- Make IdPs selection more efficient ## [22.0.11-1.10] - 2024-10-21 diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/model/IdentityProviderBean.java b/services/src/main/java/org/keycloak/forms/login/freemarker/model/IdentityProviderBean.java index 04c2ec802232..c4ca60d1a9bf 100755 --- a/services/src/main/java/org/keycloak/forms/login/freemarker/model/IdentityProviderBean.java +++ b/services/src/main/java/org/keycloak/forms/login/freemarker/model/IdentityProviderBean.java @@ -52,7 +52,7 @@ public IdentityProviderBean(RealmModel realm, KeycloakSession session, List orderedList = new ArrayList<>(); for (IdentityProviderModel identityProvider : identityProviders) { - if (identityProvider.isEnabled() && !identityProvider.isLinkOnly()) { + if (identityProvider.isEnabled() && !identityProvider.isLinkOnly() && !(identityProvider.getConfig() != null && Boolean.parseBoolean(identityProvider.getConfig().get("hideOnLoginPage")))) { addIdentityProvider(orderedList, realm, baseURI, identityProvider); } } @@ -69,12 +69,9 @@ private void addIdentityProvider(List orderedSet, RealmModel r String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider.getAlias(), realm.getName()).toString(); String displayName = KeycloakModelUtils.getIdentityProviderDisplayName(session, identityProvider); Map config = identityProvider.getConfig(); - boolean hideOnLoginPage = config != null && Boolean.parseBoolean(config.get("hideOnLoginPage")); - if (!hideOnLoginPage) { - orderedSet.add(new IdentityProvider(identityProvider.getAlias(), - displayName, identityProvider.getProviderId(), loginUrl, - config != null ? config.get("guiOrder") : null, getLoginIconClasses(identityProvider), config.get(IdentityProviderModel.LOGO_URI))); - } + orderedSet.add(new IdentityProvider(identityProvider.getAlias(), + displayName, identityProvider.getProviderId(), loginUrl, + config != null ? config.get("guiOrder") : null, getLoginIconClasses(identityProvider), config.get(IdentityProviderModel.LOGO_URI))); } // Get icon classes defined in properties of current theme with key 'kcLogoIdP-{alias}' diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java index 10d040f9c722..6967deac3dcd 100755 --- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java +++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java @@ -17,10 +17,12 @@ package org.keycloak.services.resources; import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Cookie; import jakarta.ws.rs.core.MediaType; import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.keycloak.common.util.ServerCookie; import org.keycloak.http.HttpRequest; import org.keycloak.OAuthErrorException; import org.keycloak.authentication.AuthenticationFlow; @@ -102,6 +104,7 @@ import org.keycloak.services.util.AuthenticationFlowURLHelper; import org.keycloak.services.util.BrowserHistoryHelper; import org.keycloak.services.util.CacheControlUtil; +import org.keycloak.services.util.CookieHelper; import org.keycloak.services.util.DefaultClientSessionContext; import org.keycloak.services.util.UserSessionUtil; import org.keycloak.services.validation.Validation; @@ -127,7 +130,10 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -146,6 +152,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal // Authentication session note, which references identity provider that is currently linked private static final String LINKING_IDENTITY_PROVIDER = "LINKING_IDENTITY_PROVIDER"; + private static final String KEYCLOAK_REMEMBER_IDPS = "KEYCLOAK_REMEMBER_IDPS"; private static final Logger logger = Logger.getLogger(IdentityBrokerService.class); @@ -634,7 +641,7 @@ public Response authenticated(BrokeredIdentityContext context) { } context.setToken(null); } - + StatusResponseType loginResponse = (StatusResponseType) context.getContextData().get(SAMLEndpoint.SAML_LOGIN_RESPONSE); if (loginResponse != null) { for(Iterator it = SamlSessionUtils.getSamlAuthenticationPreprocessorIterator(session); it.hasNext();) { @@ -643,7 +650,13 @@ public Response authenticated(BrokeredIdentityContext context) { } session.getContext().setClient(authenticationSession.getClient()); - + //set last login IdP to cookie (alias comma separated) + Cookie idpsCookie = session.getContext().getHttpRequest().getHttpHeaders().getCookies().get(KEYCLOAK_REMEMBER_IDPS); + List idpsAlias = idpsCookie == null ? new ArrayList<>() : Arrays.asList(idpsCookie.getValue().split(",")); + if (! idpsAlias.contains(providerId)) { + idpsAlias.add(providerId); + CookieHelper.addCookie(KEYCLOAK_REMEMBER_IDPS, idpsAlias.stream().collect(Collectors.joining(",")), AuthenticationManager.getRealmCookiePath(realmModel, session.getContext().getUri()), null, null, 31536000, realmModel.getSslRequired().isRequired(session.getContext().getConnection()), true, ServerCookie.SameSiteAttributeValue.NONE, session); + } context.getIdp().preprocessFederatedIdentity(session, realmModel, context); KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory(); realmModel.getIdentityProviderMappersByAliasStream(context.getIdpConfig().getAlias()).forEach(mapper -> {