Spring-SAML : Incoming SAML message is invalid

13,971

Solution 1

To fix this issue, set the enityBaseURL property of MetadataGenerator inside metadataGeneratorFilter bean. Code looks like this :

<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
        <constructor-arg>
            <bean class="org.springframework.security.saml.metadata.MetadataGenerator">
                <property name="entityId" value="http://myapp.com/myapp/saml/metadata"/>
                <property name="requestSigned" value="false"/>
                <property name="entityBaseURL" value="http://myapp.com/myapp"/>
            </bean>
        </constructor-arg>
    </bean>

Solution 2

A few problems I found in your securityContext.xml:

  1. entityId entity id does not need to point ot metadata. This is not a serious problem, but it's better to have your entity id more consice. It should be something like com:mycompany:my app. It is only used by IDP to identify your application.

  2. processUrl property of samlEntryPoint
    You have <property name="filterProcessesUrl" value="/saml/SSO"/> in your samlEntryPoint bean. samlEntryPoint is used when accessing /login, so you application can point you to your IDP's address. /SSO is used for when your IDP sends the message back to your application, and you application use this message to get authorization and information of the user from IDP.
    In the documentation, it says filterProcessesUrl is Url this filter should get activated on. But in your samlFilter bean you have already set samlEntryPoint on /login/**. So your setting is unnecessary and incorrect.

  3. failureRedirectHandler missing defaultFailureUrl property
    See example securityContext.xml for reference

  4. filter on pattern /saml/metadata should be metadataDisplayFilter
    You need to display your metadata when you access this page, and give this metadata to your IDP.

In all, I think it's the second point that causes the problem you have above, and it's the most sever problem you have. You should try to fix your securityContext.xml first and see if that works. If not, try use the example securityContext.xml with minimal modificaiton to make sure your applicatoin works, and then modify the file gradually to minimize risk of breaking.

Share:
13,971
Jiten
Author by

Jiten

Updated on June 23, 2022

Comments

  • Jiten
    Jiten almost 2 years

    I am facing an issue while integrating my app with SAML.

    The following is my error:

    org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication: Incoming SAML message is invalid 
    org.opensaml.common.SAMLException: Endpoint with message binding urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST and URL https://myappruldotom/saml/SSO wasn't found in local metadata at org.springframework.security.saml.util.SAMLUtil.getEndpoint(SAMLUtil.java:357) ~[spring-security-saml2-core-1.0.2.RELEASE.jar:1.0.2.RELEASE]
    

    My app is deployed on AWS and when i added the log statement in by writing the custom SAMLProcessingFilter and wrote implementation of getEndpoint() to add multiple log statements and copied the exact content of getEndpoint() method.

    The log statement shows that the endpoint is coming as IP address : MySAMLProcessingFilter.getEndpoint: MySAMLLOG - endpoint.getLocation() = https://10.193.160.123:443/mysamlapp/saml/SSO

    I have defined entityId in my SAML config, but this also didn't help. entityId in my config file as :

    <bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
        <constructor-arg>
            <bean class="org.springframework.security.saml.metadata.MetadataGenerator">
                <property name="entityId" value="https://myappruldotom/mysamlapp/saml/metadata"/>
                <property name="requestSigned" value="false"/>
            </bean>
        </constructor-arg>
    </bean>
    

    securityContext.xml file :

    <beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:security="http://www.springframework.org/schema/security"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
    
    <!-- ############################ Security Settings ############################ -->
    
    
    <!-- Enable auto-wiring -->
    <context:annotation-config/>
    
    <!-- Scan for auto-wiring classes in spring saml packages -->
    <context:component-scan base-package="org.springframework.security.saml"/>
    
    <!-- IMPORTANT!! This entry is required to enable method-level security. -->
    <security:global-method-security pre-post-annotations="enabled"></security:global-method-security>
    
    <!-- No security on resource files -->
    <security:http security="none" pattern="/resources/**" />
    
    <!-- Secured pages with SAML as entry point with SPRING CSRF filter -->
    <security:http entry-point-ref="samlEntryPoint" disable-url-rewriting="true" use-expressions="true">
        <security:csrf disabled="true"/>
        <security:intercept-url pattern="/cart/**" access="isAuthenticated()"/>
        <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
        <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
    </security:http>
    
    
    <!-- Filters for processing of SAML messages -->
    <bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
        <security:filter-chain-map request-matcher="ant">
            <security:filter-chain pattern="/saml/metadata/**" filters="metadataGeneratorFilter"/>
            <security:filter-chain pattern="/login/**" filters="samlEntryPoint"/>
            <security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
            <security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
            <security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
            <security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
        </security:filter-chain-map>
    </bean>
    
    <bean id="successRedirectHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
        <property name="defaultTargetUrl" value="/cart"/>
    </bean>
    
    <!-- After logout, show the logout success page -->
    <bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
        <property name="defaultTargetUrl" value="/"/>
    </bean>
    
    <!-- Logout handler terminating local session -->
    <bean id="logoutHandler" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
        <property name="invalidateHttpSession" value="true"/>
    </bean>
    
    <!-- Handler deciding where to redirect user after failed login -->
    <bean id="failureRedirectHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
        <property name="useForward" value="true"/>
    </bean>
    
    <security:authentication-manager alias="authenticationManager">
        <!-- Register authentication manager for SAML provider -->
        <security:authentication-provider ref="samlAuthenticationProvider"/>
    </security:authentication-manager> 
    
    <!-- Central storage of cryptographic keys -->
    
    <bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
        <constructor-arg value="classpath:samlKeystore.jks"/>
        <constructor-arg type="java.lang.String" value="nalle123"/>
        <constructor-arg>
            <map>
                <entry key="apollo" value="nalle123"/>
            </map>
        </constructor-arg>
        <constructor-arg type="java.lang.String" value="apollo"/>
    </bean>
    
    <!-- Logger for SAML messages and events -->
    <bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
    
    <!-- IDP Discovery Service -->
    <bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery"></bean>
    
    
    <bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
      <constructor-arg>
         <bean class="org.springframework.security.saml.metadata.MetadataGenerator">
            <property name="entityId" value="https://myappdotcom/mysamlapp/saml/metadata"/>
            <property name="requestSigned" value="false"/>
        </bean>
    </constructor-arg>
    </bean>
    
    <!-- Entry point to initialize authentication, default values taken from properties file -->
    <bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
       <property name="filterProcessesUrl" value="/saml/SSO"/>
       <property name="defaultProfileOptions">
        <bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
            <property name="includeScoping" value="false"/>
        </bean>
    </property>
    </bean>
    
    <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
       <property name="defaultIDP" value="https://dev-942345.oktapreview.com/"/>
    
       <constructor-arg>
        <list>
            <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
                <constructor-arg>
                    <value type="java.io.File">#{systemProperties['catalina.home']}/saml/idp.xml</value>
                </constructor-arg>
                <property name="parserPool" ref="parserPool"/>
            </bean>                
        </list>
    </constructor-arg>
    </bean>
    
    
    <!-- SAML Authentication Provider responsible for validating of received SAML messages -->
    <bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
       <property name="userDetails" ref="samlUserDetailsService" />
       <property name="forcePrincipalAsString" value="false" />
    </bean>
    
    
    <!-- Custom user details service to attach app specific roles to federated identities -->
    <bean id="samlUserDetailsService" class="com.zap.shop.SAMLUserDetailsServiceImpl"></bean>
    
    <!-- Provider of default SAML Context -->
    <bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/>
    
    <!-- Processing filter for WebSSO profile messages -->
    <bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
        <property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
    </bean>
    
    
    <!-- Override default logout processing filter with the one processing SAML messages -->
    <bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter">
        <constructor-arg index="0" ref="successLogoutHandler"/>
        <constructor-arg index="1" ref="logoutHandler"/>
        <constructor-arg index="2" ref="logoutHandler"/>
    </bean>
    
    <!-- Filter processing incoming logout messages -->
    <!-- First argument determines URL user will be redirected to after successful global logout -->
    <bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter">
        <constructor-arg index="0" ref="successLogoutHandler"/>
        <constructor-arg index="1" ref="logoutHandler"/>
    </bean>
    
    <!-- Class loading incoming SAML messages from httpRequest stream -->
    <bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
        <constructor-arg>
            <list>
                <ref bean="redirectBinding"/>
                <ref bean="postBinding"/>
                <ref bean="artifactBinding"/>
                <ref bean="soapBinding"/>
                <ref bean="paosBinding"/>
            </list>
        </constructor-arg>
    </bean>
    
    <!-- SAML 2.0 WebSSO Assertion Consumer -->
    <bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>
    
    <!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
    <bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
    
    <!-- SAML 2.0 Web SSO profile -->
    <bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>
    
    <!-- SAML 2.0 Holder-of-Key Web SSO profile -->
    <bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
    
    <!-- SAML 2.0 ECP profile -->
    <bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>
    
    <!-- SAML 2.0 Logout Profile -->
    <bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/>
    
    <!-- Bindings, encoders and decoders used for creating and parsing messages -->
    <bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
        <constructor-arg ref="parserPool"/>
        <constructor-arg ref="velocityEngine"/>
    </bean>
    
    <bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
        <constructor-arg ref="parserPool"/>
    </bean>
    
    <bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding">
        <constructor-arg ref="parserPool"/>
        <constructor-arg ref="velocityEngine"/>
        <constructor-arg>
            <bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl">
                <constructor-arg>
                    <bean class="org.apache.commons.httpclient.HttpClient">
                        <constructor-arg>
                            <bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
                        </constructor-arg>
                    </bean>
                </constructor-arg>
                <property name="processor">
                    <bean class="org.springframework.security.saml.processor.SAMLProcessorImpl">
                        <constructor-arg ref="soapBinding"/>
                    </bean>
                </property>
            </bean>
        </constructor-arg>
    </bean>
    
    <bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding">
        <constructor-arg ref="parserPool"/>
    </bean>
    
    <bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding">
        <constructor-arg ref="parserPool"/>
    </bean>
    
    <!-- Initialization of OpenSAML library-->
    <bean class="org.springframework.security.saml.SAMLBootstrap"/>
    
    <!-- Initialization of the velocity engine -->
    <bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/>
    
    <!-- XML parser pool needed for OpenSAML parsing -->
    <bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize">
        <property name="builderFeatures">
            <map>
                <entry key="http://xml.org/sax/features/external-general-entities" value="false"/>
                <entry key="http://javax.xml.XMLConstants/feature/secure-processing" value="true"/>
                <entry key="http://apache.org/xml/features/disallow-doctype-decl" value="true"/>
            </map>
        </property>
    </bean>
    
    <bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/> 
    </beans>
    

    Thanks for your help in advance.