I am trying to integrate SAML SSO with Spring Security using Spring Security SAML extension. Before, I succeeded to run a proof of concept found here: https://github.com/vdenotaris/spring-boot-security-saml-sample. Unfortunately, after moving the configuration to my project it is not working correctly.
After analyzing the logs, I figured out that my application (SP) is correctly downloading the IdP metadata from provided URL. However, after trying to download metadata of my SP by trying https://localhost:8443/saml/metadata in browser, the following exception is thrown:
javax.servlet.ServletException: Error initializing metadata
at org.springframework.security.saml.metadata.MetadataDisplayFilter.processMetadataDisplay(MetadataDisplayFilter.java:120)
at org.springframework.security.saml.metadata.MetadataDisplayFilter.doFilter(MetadataDisplayFilter.java:88)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1645)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:564)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1111)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:498)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1045)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:98)
at org.eclipse.jetty.server.Server.handle(Server.java:461)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:284)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:244)
at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:534)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.saml2.metadata.provider.MetadataProviderException: No hosted service provider is configured and no alias was selected
at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalEntity(SAMLContextProviderImpl.java:311)
at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalContext(SAMLContextProviderImpl.java:216)
at org.springframework.security.saml.context.SAMLContextProviderImpl.getLocalEntity(SAMLContextProviderImpl.java:107)
at org.springframework.security.saml.metadata.MetadataDisplayFilter.processMetadataDisplay(MetadataDisplayFilter.java:114)
... 24 more
After debugging, I was not able to figure out why Spring is not able to figure out entity ID of my application. I am setting it like this:
// Filter automatically generates default SP metadata
@Bean
public MetadataGenerator metadataGenerator() {
MetadataGenerator metadataGenerator = new MetadataGenerator();
metadataGenerator.setEntityId(environment.getRequiredProperty("saml.entity-id"));
metadataGenerator.setEntityBaseURL("URL is here");
metadataGenerator.setExtendedMetadata(extendedMetadata());
metadataGenerator.setIncludeDiscoveryExtension(false);
metadataGenerator.setKeyManager(keyManager());
return metadataGenerator;
}
Of course the saml.entity-id property is correctly downloaded from my configuration. Whole security config is here: https://gist.github.com/mc-suchecki/671ecb4d5ae4bae17f81
Order of the filters is correct - the Metadata Generator Filter is before the SAML Filter. I am not sure that is relevant - I suppose not - but my application is not using Spring Boot - and the sample application (the source of the configuration) is.
Thank you in advance for any help.
I found the issue this week. There was a problem with filters. One of the methods was creating the 'samlFilter', like this:
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"), samlEntryPoint()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"), samlLogoutFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
metadataDisplayFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
samlWebSSOProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
samlWebSSOHoKProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
samlLogoutProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/discovery/**"), samlIDPDiscovery()));
return new FilterChainProxy(chains);
}
After that, another method was setting the whole filter chain for Spring, like so:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().authenticationEntryPoint(samlEntryPoint());
http.csrf().disable();
http.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
.addFilterAfter(samlFilter(), BasicAuthenticationFilter.class);
http.authorizeRequests().antMatchers("/").permitAll().antMatchers("/error").permitAll()
.antMatchers("/saml/**").permitAll().anyRequest().authenticated();
http.logout().logoutSuccessUrl("/");
}
That was perfectly correct. However, when I was launching the application using Jetty server, I was trying to connect only the 'samlFilter' to the application context. Because of that, the 'metadataGeneratorFilter' that is required to be before the 'metadataDisplayFilter' was not added to the filter chain at all. When I changed 'samlFilter' to 'springSecurityFilter', everything started working. That was not easy to find, because of my non-standard use of Jetty.
Thank you for your help!