mc.suchecki
mc.suchecki

Reputation: 1898

Spring Security SAML plugin - No hosted service provider is configured exception

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.

Upvotes: 18

Views: 11426

Answers (5)

mluelmo
mluelmo

Reputation: 39

I've found this exception (No hosted provider is configured). I was trying to work with a pre-configured metadata. If you want to work with a pre-configured metadata, you don't need metadataGeneratorFilter, so you can delete it from your configuration (commented on this sample code):

@Override
    protected void configure(HttpSecurity http) throws Exception {
        // TODO Auto-generated method stub
        http.authorizeRequests()
            .antMatchers("/logout.jsp").permitAll()
            .antMatchers("/loginFailed.jsp").permitAll()
            .antMatchers("/saml/**").permitAll()
        .anyRequest()
                .authenticated()
                .and().httpBasic().authenticationEntryPoint(samlEntryPoint())
                .and().requiresChannel().anyRequest().requiresInsecure();
        
        http
            //.addFilterBefore(metadataGeneratorFilter, ChannelProcessingFilter.class)
            .addFilterAfter(samlFilter, BasicAuthenticationFilter.class);
        
        http.csrf().disable();
                
    }

the next thing you have to do, is to add preconfigured metadata. You can do this as a bean on the root of your securityConfig.xml, or adding the bean on your cachingMetadataManager (both with xml configuration). And here were the problem. If you choose preconfigured metadata, you have to add extendedMetadata for this bean, specifying it as local:

    <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
    <constructor-arg>
        <list>
            <!-- PreConfigured SP MetaData -->
            <bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
                <constructor-arg>
                    <bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
                        <constructor-arg>
                            <bean class="java.util.Timer"/>
                        </constructor-arg>
                        <constructor-arg>
                            <bean class="org.opensaml.util.resource.ClasspathResource">
                                <constructor-arg value="/metadata/spMetadata.xml"/>
                            </bean>
                        </constructor-arg>
                        <property name="parserPool" ref="parserPool"/>
                    </bean>
                </constructor-arg>
                <constructor-arg>
                    <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                        <property name="local" value="true"/>
                        <property name="alias" value="metadataAlias"/>
                    </bean>
                </constructor-arg>
            </bean>
            <!-- IP MetaData -->

Upvotes: 1

itzg
itzg

Reputation: 1111

I was able to resolve this by excluding the SAML filters from Spring Boot's auto-registration of regular (non-security) filters by adding a FilterRegistrationBean for each filter, such as

  @Bean
  public FilterRegistrationBean disableSAMLEntryPoint() {
    final FilterRegistrationBean registration = 
        new FilterRegistrationBean<>(samlEntryPoint());
    registration.setEnabled(false);
    return registration;
  }

Upvotes: 0

mc.suchecki
mc.suchecki

Reputation: 1898

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!

Upvotes: 8

Antonio
Antonio

Reputation: 654

In MetadataGenerator entityId is a shared key you use to communicate to your IDP that your application wants to access it. On IDP side there is a samlConfiguration where you need to enter the same entityId to enable your application to access to the IDP users.

<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
    <bean class="org.springframework.security.saml.metadata.MetadataGenerator">              
        <property name="entityId" value="****"/>
        <property name="extendedMetadata">
            <bean class="org.springframework.security.saml.web.MyExtendedMetadata">
                <property name="signMetadata" value="true"/>                        
                <property name="signingKey" value="****"/>
                <property name="encryptionKey" value="****"/>
            </bean>
        </property>
    </bean>
</constructor-arg>

Upvotes: 2

Ruddy
Ruddy

Reputation: 89

Do you have an IDP configured?

Upvotes: 1

Related Questions