rwblackburn
rwblackburn

Reputation: 371

Spring SAML Alias is used both for entity and SP

I am new to SAML, and am integrating it into a Spring web application. I started by following the Spring SAML quick-start guide found here: http://docs.spring.io/spring-security-saml/docs/1.0.0.RELEASE/reference/html/chapter-quick-start.html

I got a this running fine. I then wanted to switch to target IDP, which is already running and successfully serving other SPs within the company.

In my securityContext.xml, I added the following to the metadata bean:

    <bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
        <constructor-arg>
            <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
                <constructor-arg>
                    <value type="java.io.File">classpath:security/MyEntityId_sp.xml</value>
                </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="myAlias"/>
               <property name="securityProfile" value="metaiop"/>
               <property name="sslSecurityProfile" value="pkix"/>
               <property name="signingKey" value="apollo"/>
               <property name="encryptionKey" value="apollo"/>
               <property name="requireArtifactResolveSigned" value="false"/>
               <property name="requireLogoutRequestSigned" value="false"/>
               <property name="requireLogoutResponseSigned" value="false"/>
               <property name="idpDiscoveryEnabled" value="false"/>
            </bean>
        </constructor-arg>
    </bean>

    <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
        <constructor-arg>
            <value type="java.io.File">classpath:security/IDP-MetaData.xml</value>
        </constructor-arg>
        <property name="parserPool" ref="parserPool"/>
    </bean>

Using the Firefox SAML tracer, I can see that the interactions work fine, and the IDP redirect back to my app (the SP) after authenticating the user, with a valid SAML XML packet. This is not surprising since this IDP is already successfully being used by other SPs. However, it is redirecting to "http://localhost:8080/saml-demo/saml/SSO/alias/myAlias" which in turn throws the following error to the browser...

 Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Error determining metadata contracts

Digging into the code a bit further with the Java debugger, I found the real error is deeper in the code. It is thrown by org.springframework.security.saml.metadata.MetadataManager on line 913 when it sees the same alias for both the SP and the IDP. The real error is:

 MetadataProviderException: Alias myAlias is used both for entity MyEntityId and MyEntityId

This only other change beyond what the quick start guide has is to the metadataGeneratorFilter:

<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
    <constructor-arg>
        <bean class="org.springframework.security.saml.metadata.MetadataGenerator">
            <property name="entityId" value="MyEntityId"/>
            <property name="signMetadata" value="false"/>
        </bean>
    </constructor-arg>
</bean> 

The IDP metadata file contains ...

<md:EntityDescriptor entityID="MyEntityId" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
  <md:IDPSSODescriptor WantAuthnRequestsSigned="0" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <md:KeyDescriptor use="signing">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>xxxxxx</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://auth.myidp.com/sa1234/" index="0" isDefault="1" />
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://auth.myidp.com/sa1234/" />
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://auth.myidp.com/sa1234/" />
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://auth.myidp.com/sa1234/" />
    <md:ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://auth.myidp.com/sa1234/" />
    <md:ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://auth.myidp.com/sa1234/" />
    <md:ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://auth.myidp.com/sa1234/" />
    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://auth.myidp.com/sa1234/" />
    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://auth.myidp.com/sa1234/" />
    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://auth.myidp.com/sa1234/" />
  </md:IDPSSODescriptor>
  <md:AttributeAuthorityDescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <md:KeyDescriptor use="signing">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>xxxxxx</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:AttributeService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://auth.myidp.com/sa1234/" />
  </md:AttributeAuthorityDescriptor>
</md:EntityDescriptor>

Here is the contents of MyEntityId_sp.xml:

<?xml version="1.0" encoding="UTF-8"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
    ID="MyEntityId" entityID="MyEntityId">
    <md:SPSSODescriptor AuthnRequestsSigned="true"
        WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
        <md:KeyDescriptor use="signing">
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:X509Data>
                    <ds:X509Certificate>xxx</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </md:KeyDescriptor>
        <md:KeyDescriptor use="encryption">
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:X509Data>
                    <ds:X509Certificate>xxx</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </md:KeyDescriptor>
        <md:SingleLogoutService
            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
            Location="http://localhost:8080/saml-demo/saml/SingleLogout/alias/myAlias" />
        <md:SingleLogoutService
            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
            Location="http://localhost:8080/saml-demo/saml/SingleLogout/alias/myAlias" />
        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
        </md:NameIDFormat>
        <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient
        </md:NameIDFormat>
        <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
        </md:NameIDFormat>
        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
        </md:NameIDFormat>
        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
        </md:NameIDFormat>
        <md:AssertionConsumerService
            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
            Location="http://localhost:8080/saml-demo/saml/SSO/alias/myAlias"
            index="0" isDefault="true" />
        <md:AssertionConsumerService
            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8080/saml-demo/saml/SSO/alias/myAlias"
            index="1" />
    </md:SPSSODescriptor>
</md:EntityDescriptor>

Upvotes: 1

Views: 4291

Answers (1)

rwblackburn
rwblackburn

Reputation: 371

I was able to track down the issue.

The IDP had an audience restriction that used the same ID ad the EntityID of the IDP itself. I could see this in the IDP meta data file:

<md:EntityDescriptor entityID="MyEntityId" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">

Then using the Firefox SAML trace add on I could see the final responce from the IDP contained:

<saml:AudienceRestriction> <saml:Audience>MyEntityId</saml:Audience> </saml:AudienceRestriction>

Obviously "MyEntityID" is not what was literally used, it was a keyword for this app that made sense in the context of the files. However, this meant I needed to give my SP entity the same ID as this AudienceRestriction, or else I would get an error. However, As Andrew K. pointed out, you cannot give two entities the same entity ID.

I was not the one to set up the IDP server, so I do not have control over some of this (and the IDP metadata file was provided to me, so I just assumed there was nothign wrong with it). However, to solve this, I simply changed the entityID in the IDP metadata file. I'm new to SAML, but I don't think this will have any negative impacts for my specific implementation.

Upvotes: 2

Related Questions