How to configure spring security saml to authenticate with multiple idp choosen by users

29 views Asked by At

We have an application which uses spring security saml for SSO authentication for a client. We needed to add a new SSO authentication for our internal users on the login page, so user_client need to click on sso_login_client button to be redirected on its IdP to log in, and user_internal on sso_login_internal button to be redirected on our IdP.

The problem is that our configuration is implemented for only one IdP. What do we need to change to support another IdP ?

Here is our configuration :

SecurityConfig.java

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig {
    
    @Configuration
    @Order(1)
    @Profile("SSO")
    public static class WebSecurityConfig extends WebSecurityConfigurerAdapter implements InitializingBean, DisposableBean {
       
        private static final Logger logger = Logger.getLogger(SecurityConfig.class);
        private Timer backgroundTaskTimer;
        private MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager;
        
        private String metadataUrl = null;
        private String entityID = null;
        private String entityBaseURL = null;
        private String metadataFile = null;
        private String metadataRetrievalMode = null;
        private Long maxAuthenticationAge = null;
        
    ...
     
        // SAML Authentication Provider responsible for validating of received SAML
        // messages
        @Bean
        public CustomSamlAuthenticationProvider samlAuthenticationProvider() {
            CustomSamlAuthenticationProvider customSamlAuthenticationProvider = new CustomSamlAuthenticationProvider();
            customSamlAuthenticationProvider.setForcePrincipalAsString(false);
            return customSamlAuthenticationProvider;
        }
     
    ...
        
        // Setup advanced info about metadata
        @Bean
        public ExtendedMetadata extendedMetadata() {
                ExtendedMetadata extendedMetadata = new ExtendedMetadata();
                extendedMetadata.setIdpDiscoveryEnabled(false); 
                extendedMetadata.setSignMetadata(false);
                extendedMetadata.setEcpEnabled(true);
                return extendedMetadata;
        }
        
        
        @Bean
        @Qualifier("idp-adfs")
        public ExtendedMetadataDelegate adfsExtendedMetadataProvider()
                throws MetadataProviderException {
            ExtendedMetadataDelegate extendedMetadataDelegate = null;
            
            if ("FILE".equals(metadataRetrievalMode)) {
                   FilesystemMetadataProvider fileSystemMetadataProvider = new FilesystemMetadataProvider(new File(metadataFile));
                    fileSystemMetadataProvider.setParserPool(parserPool());
                    
                    extendedMetadataDelegate = new ExtendedMetadataDelegate(fileSystemMetadataProvider, extendedMetadata());
                    extendedMetadataDelegate.setMetadataTrustCheck(false);
                    extendedMetadataDelegate.setMetadataRequireSignature(false);
            } else {
                 String idpKeycloakMetadataURL = metadataUrl;
                 HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(
                            this.backgroundTaskTimer, httpClient(), idpKeycloakMetadataURL);
                httpMetadataProvider.setParserPool(parserPool());
                
                extendedMetadataDelegate = new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
                // Disable metadata trust check to prevent "Signature trust establishment failed for metadata entry" exception
                extendedMetadataDelegate.setMetadataTrustCheck(false);
                extendedMetadataDelegate.setMetadataRequireSignature(false);
            }
            
            backgroundTaskTimer.purge();
            return extendedMetadataDelegate;
        }

        // IDP Metadata configuration - paths to metadata of IDPs in circle of trust
        // is here
        // Do no forget to call iniitalize method on providers
        @Bean
        @Qualifier("metadata")
        public CachingMetadataManager metadata() throws MetadataProviderException {
            
              List<MetadataProvider> providers = new ArrayList<MetadataProvider>();
                providers.add(adfsExtendedMetadataProvider());
                CachingMetadataManager metadataManager=new CachingMetadataManager(providers);
                metadataManager.setDefaultIDP(metadataUrl);
                return new CachingMetadataManager(providers);
        }
     
        // Filter automatically generates default SP metadata
        @Bean
        public MetadataGenerator metadataGenerator() {
            MetadataGenerator metadataGenerator = new MetadataGenerator();
            metadataGenerator.setEntityId(entityID);
            if (entityBaseURL != null) {
                metadataGenerator.setEntityBaseURL(entityBaseURL);
            }
            metadataGenerator.setExtendedMetadata(extendedMetadata());
            metadataGenerator.setIncludeDiscoveryExtension(false);
            metadataGenerator.setKeyManager(keyManager()); 
            metadataGenerator.setRequestSigned(false);
            return metadataGenerator;
        }
     

    ...
         
        @Bean
        public SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter() throws Exception {
            SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter = new SAMLWebSSOHoKProcessingFilter();
            samlWebSSOHoKProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
            samlWebSSOHoKProcessingFilter.setAuthenticationManager(authenticationManager());
            samlWebSSOHoKProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
            return samlWebSSOHoKProcessingFilter;
        }
        
        // Processing filter for WebSSO profile messages
        @Bean
        public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception {
            SAMLProcessingFilter samlWebSSOProcessingFilter = new CustomSamlProcessingFilter();
            samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager());
            samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
            samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
            return samlWebSSOProcessingFilter;
        }
         
        @Bean
        public MetadataGeneratorFilter metadataGeneratorFilter() {
            return new MetadataGeneratorFilter(metadataGenerator());
        }
    
        @Override  
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable();
            http
                .httpBasic()
                    .authenticationEntryPoint(samlEntryPoint());      
            http
                    .addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
                    .addFilterAfter(samlFilter(),BasicAuthenticationFilter.class)
                    .addFilterBefore(samlFilter(), CsrfFilter.class);
            http .requestMatchers()        
                .antMatchers("/loginSSO/**","/saml/**").and().authorizeRequests()
                    .anyRequest().authenticated();
            http
                    .logout()
                    .disable(); // The logout procedure is already handled by SAML filters.
            
        }
     
        
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth
                .authenticationProvider(samlAuthenticationProvider());
        }
        
    }
}

LoginSSOController.java

@Controller
public class LoginSSOController {
    
    // Logger
        private static final Logger logger = Logger.getLogger(LoginSSOController.class);

        @RequestMapping(value = "/loginSSO", method = RequestMethod.GET)
        public View idpSelection(HttpServletRequest request, Model model) {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if (auth == null) {
                logger.debug("Current authentication instance from security context is null");
            }else {
                logger.debug("Current authentication instance from security context: ");
            }
            if (auth == null) {
                RedirectView redirect = new RedirectView("/login");
                redirect.setExposeModelAttributes(false);
                return redirect;
            }
            RedirectView redirect = new RedirectView("/dashboard");
            redirect.setExposeModelAttributes(false);
            return redirect;
        }
}

login.html

<a href="/loginSSO" class="logAD btn"><i class="fa fa-windows">
    </i> <span th:text="${loginPageSSOLabel}">Login with SSO</span>
</a>

I heard about RelyingPartyRegistration but i don't know if it's the right solution for our configuration and how to implement it.

Also i am wondering how does spring-security distinguish multiple IdPs for different users ? Do we need to create a new endpoint like "/loginSSO" ?

Thank you for your help in advance.

0

There are 0 answers