Announcement Announcement Module
Collapse
No announcement yet.
IDP initiated SAML sign on with Multi Tenant SP configuration Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • IDP initiated SAML sign on with Multi Tenant SP configuration

    I have a working setup of an IDP initiated SAML sign on to our single SP. The metadata configuration is as shown:

    Code:
    <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
            <constructor-arg>
                <list>
                    <!-- Local metadata for SP: certificate + key data for SP initiated calls. Doesn't look the keys are needed for the current IDP initiated SAML login -->
                    <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
                        <constructor-arg>
                            <value type="java.io.File">classpath:sp-metadata.xml</value>
                        </constructor-arg>
                        <property name="parserPool" ref="parserPool"/>
                    </bean>
                    <!-- IDP metadata: configured with IDP provided certificate and public key data -->
                    <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
                        <constructor-arg>
                            <value type="java.io.File">classpath:idp-metadata.xml</value>
                        </constructor-arg>
                        <property name="parserPool" ref="parserPool"/>
                    </bean>
                </list>
            </constructor-arg>
            <property name="defaultExtendedMetadata">
                <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                    <property name="local" value="true"/> <!-- Indicates configuration for local SP -->
                    <property name="alias" value="${saml_sp_alias}"/>
                    <property name="securityProfile" value="metaiop"/>
                    <property name="requireArtifactResolveSigned" value="false"/>
                    <property name="requireLogoutRequestSigned" value="false"/>
                    <property name="requireLogoutResponseSigned" value="false"/>
                    <property name="idpDiscoveryEnabled" value="false"/>
                </bean>
            </property>
            <property name="hostedSPName" value="${saml_sp_alias}"/>
        </bean>
    With the above setup, the application parses the token(binding used is HTTP-POST) authenticates and logs in the user. So far so good. Now i need to be able to support multiple IDPs and SPs for my one application. SAML sign on will still be IDP initiated. I read from the documentation that Multi Tenant SP configuration is possible by using the ExtendedMetaDataDelegate bean. I'm just not sure as to how i need to use it. I tried a few things:
    1. I added new "FilesystemMetadataProvider" beans with metadata for the new SP configuration to the existing CachingMetadataManager bean, but it seems like the assertion validation fails because the expected audience does not match the value in the "hostedSPName" of my metadata configuration (now that i have multiple SPs).

    2. I added a new bean (in addition to the cachingMetaDataManager):

    Code:
    <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:sp-metadata-2.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="sp-2"/>
                    <property name="securityProfile" value="metaiop"/>
                    <property name="requireArtifactResolveSigned" value="false"/>
                    <property name="requireLogoutRequestSigned" value="false"/>
                    <property name="requireLogoutResponseSigned" value="false"/>
                    <property name="idpDiscoveryEnabled" value="false"/>
                </bean>
            </constructor-arg>
        </bean>
    With this i don't even see the assertion being authenticated.

    I'm new to this and spent a few days looking at the code and the documentation to try and figure things out. We currently use Spring security and i was very happy to find that SAML integration is possible with Spring security. I'm trying to stack SAML authentication on top of other GUI + Siteminder authentication that we currently support.
    Thanks to the SAML dev team for all the great work on this. I'd appreciate a little guidance in trying to figure this problem out.
    Last edited by jkookie; May 9th, 2013, 12:25 PM. Reason: Formatting

  • #2
    Ok, Haven't had the chance to test with multiple IDPs yet, but i think i know what I'm doing wrong. I think I should be using one metadata.xml file for all my Service Provider configurations. Each SP should be in its own <EntityDescriptor> wrapped in an <EntitiesDescriptor> element.
    I've been looking at it like i need to have a separate matadata file for each entity (SPs and IDPs) and I think that's what is wrong with my approach.
    Hopefully this works.

    Comment


    • #3
      I think the right thing to do is define multiple SPs in one metadata.xml, but not sure it will work. I tried a simple test where i included 2 ServiceProvider Entities in the metadata, defined one of them as the hostedSPName (property of the CachcingDataManager) ,as the documentation states that this is a required property if multiple SPs are going to be used. So:
      Code:
      <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
              <constructor-arg>
                  <list>
                      <!-- Local metadata for SP: certificate + key data for SP initiated calls. Doesn't look the keys are needed for the current IDP initiated SAML login -->
                      <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
                          <constructor-arg>
                              <value type="java.io.File">classpath:sp-metadata.xml</value>
                          </constructor-arg>
                          <property name="parserPool" ref="parserPool"/>
                      </bean>
                      <!-- IDP metadata: configured with IDP provided certificate and public key data -->
                      <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
                          <constructor-arg>
                              <value type="java.io.File">classpath:idp-metadata.xml</value>
                          </constructor-arg>
                          <property name="parserPool" ref="parserPool"/>
                      </bean>
                  </list>
              </constructor-arg>
              <property name="defaultExtendedMetadata">
                  <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                      <property name="local" value="true"/> <!-- Indicates configuration for local SP -->
                      <property name="alias" value="${saml_sp_alias}"/>
                      <property name="securityProfile" value="metaiop"/>
                      <property name="requireArtifactResolveSigned" value="false"/>
                      <property name="requireLogoutRequestSigned" value="false"/>
                      <property name="requireLogoutResponseSigned" value="false"/>
                      <property name="idpDiscoveryEnabled" value="false"/>
                  </bean>
              </property>
              <property name="hostedSPName" value="${saml_sp_alias}"/>
          </bean>
      I processed a IDP initiated authentication assertion for the SP that is not defined as my "hostedSP", but is defined in my sp-metadata file as one of the 2 SPs. I get the following exception
      Code:
      org.opensaml.common.SAMLException: Our entity is not the intended audience of the assertion
              at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.verifyAssertionConditions(WebSSOProfileConsumerImpl.java:471) ~[spring-security-saml2-core-1.0.0-RC2.jar:1.0.0.RC2]
              at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.verifyAssertion(WebSSOProfileConsumerImpl.java:299) ~[spring-security-saml2-core-1.0.0-RC2.jar:1.0.0.RC2]
              at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:204) ~[spring-security-saml2-core-1.0.0-RC2.jar:1.0.0.RC2]
              at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:81) [spring-security-saml2-core-1.0.0-RC2.jar:1.0.0.RC2]
      I see that the code in the class WebSSOProfileConsumerImpl where it fails does this:
      Code:
      if (context.getLocalEntityId().equals(aud.getAudienceURI()))
      At this point i still am not sure how authentication with multiple SPs will work. A context has one localEntityID and now that I Have
      2 SPs, do i need to have 2 contexts? with authentication iterating through the contexts. Not really sure what i'm missing here.
      Also i'm still not sure how to use the ExtendedMetaDataDelegate bean for this purpose.

      Comment


      • #4
        Hi,

        I'm using it in a similar multi-tennant setup that you are, except I've customized the behaviour a bit. For example, I've written my own metadata provider and manager that extend the general classes to load the metadata from a database. I also have exdended the details service to load the user from the correct schema etc. I have both IDP and SP metadata for each customer, as well as the extended metadata for each. Each customer has their own alias so the framework knows which IDP/SP to choose based on the alias. It is also necessary to use the relayState when doing SP initiated connections to pass context information so you have it.

        Comment


        • #5
          Thanks for the comment Ian. I've customized a few classes as well. I'm coming to the conclusion that i'll have to customize filling the SAMLContext too, I was hoping that i could somehow use the SAMLContextProvider Implementation out of the box, but i suspect i'll have to customize it.
          There's much i do not know and i'm figuring things out as i go. Since my scenario is an IDP initiated sign on, i will not have any information in the relaystate parameter. In case of multiple SPs, I guess the way for the SAML implementation to figure out which SP should be validated against is to check for the 'alias/sp-name' pattern in the URL and retrieve the SP ID from there.
          I'm going to look into it a little more and then will probably have to ask the IDP to specify an SP in the URL when my app receives the assertion.

          Comment


          • #6
            SAMLContextProvider

            Originally posted by jkookie View Post
            Thanks for the comment Ian. I've customized a few classes as well. I'm coming to the conclusion that i'll have to customize filling the SAMLContext too, I was hoping that i could somehow use the SAMLContextProvider Implementation out of the box, but i suspect i'll have to customize it.
            There's much i do not know and i'm figuring things out as i go. Since my scenario is an IDP initiated sign on, i will not have any information in the relaystate parameter. In case of multiple SPs, I guess the way for the SAML implementation to figure out which SP should be validated against is to check for the 'alias/sp-name' pattern in the URL and retrieve the SP ID from there.
            I'm going to look into it a little more and then will probably have to ask the IDP to specify an SP in the URL when my app receives the assertion.
            Yes, I had to override a few methods in that class as well, specifically:

            populatePeerEntityId() and populateLocalEntityId() to populate the local and peer ids and roles.

            Code:
            context.setPeerEntityId( provider.getIdpEntityId() );
            context.setPeerEntityRole( IDPSSODescriptor.DEFAULT_ELEMENT_NAME )
            where provider is a domain class that I have looked up based on the information in the attribute, although this is only used for an SP initiated login, the framework can figure out which provider to use if it is IDP initiated.

            For IDP initiated sessions, I use an attribute of the assertion that contains the info required so I know which customer is making the request, although using the URL would also work. Most IDPs allow you to customize this so it works well.

            Cheers,
            Ian

            Comment


            • #7
              I think i figured out my configuration for Mulitple IDPs and SPs for an IDP initiated scenario. My metadata configuration -which was confusing for me to figure out - is
              Code:
              <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
                      <constructor-arg>
                          <list>
                              <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:multiple-sps.xml</value>
                                          </constructor-arg>
                                          <property name="parserPool" ref="parserPool"/>
                                      </bean>
                                  </constructor-arg>
                                  <constructor-arg>
                                      <map>
                                          <entry key="sp-1">
                                              <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                                                  <property name="local" value="true"/>
                                                  <property name="alias" value="sp-1"/>
                                                  <property name="securityProfile" value="metaiop"/>
                                                  <property name="requireArtifactResolveSigned" value="false"/>
                                                  <property name="requireLogoutRequestSigned" value="false"/>
                                                  <property name="requireLogoutResponseSigned" value="false"/>
                                                  <property name="idpDiscoveryEnabled" value="false"/>
                                              </bean>
                                          </entry>
                                          <entry key="sp-2">
                                              <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                                                  <property name="local" value="true"/>
                                                  <property name="alias" value="sp-2"/>
                                                  <property name="securityProfile" value="metaiop"/>
                                                  <property name="requireArtifactResolveSigned" value="false"/>
                                                  <property name="requireLogoutRequestSigned" value="false"/>
                                                  <property name="requireLogoutResponseSigned" value="false"/>
                                                  <property name="idpDiscoveryEnabled" value="false"/>
                                              </bean>
                                          </entry>
                                      </map>
                                  </constructor-arg>
                              </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:mulitple-idps.xml</value>
                                          </constructor-arg>
                                          <property name="parserPool" ref="parserPool"/>
                                      </bean>
                                  </constructor-arg>
                              </bean>
                          </list>
                      </constructor-arg>
                      <property name="defaultExtendedMetadata">
                          <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                              <property name="local" value="true"/> 
                              <property name="alias" value="sp-2"/>
                              <property name="securityProfile" value="metaiop"/>
                              <property name="requireArtifactResolveSigned" value="false"/>
                              <property name="requireLogoutRequestSigned" value="false"/>
                              <property name="requireLogoutResponseSigned" value="false"/>
                              <property name="idpDiscoveryEnabled" value="false"/>
                          </bean>
                      </property>
                      <property name="hostedSPName" value="sp-2"/>
                  </bean>
              I've requested the IDP to include the SP in the URL in the form of "/alias/<SP-ID>". With this it looks like the SAMLContextProviderImpl parses the right Local ID from the URL into the context. If they come back and tell me they cannot, I'll most likely create a custom SAMLContextProviderImpl.
              Thanks again Ian, for your comments and suggestions.

              Comment


              • #8
                Hi,

                You have figured most of the bits already, but perhaps some of the stuff bellow will still help you.

                You don't need to put all your local SP configurations into a single metadata file. You also very likely want to provide a different local SP to each of your IDPs, so having them all together in one file makes this more difficult. Using one big metadata file is equivalent to having them included one at a time.

                The multitenancy works as follows - each instance of your local SP has it's own unique 'alias' property which tells the system to which of the SPs the incoming request belongs to based on the URL (e.g. request coming to http://serverort/saml/SSO/alias/sp1 vs. http://serverort/saml/SSO/alias/sp2). You share exactly one SP metadata with each of your IDPs and therefore whenever IDP sends a request/response to your SP it will include URL which tells which of the local SP instances to use = you always have exactly one SP in your context. The SAMLContextProvider is responsible for this.

                You don't need to set your hostedSP property at all, system will correctly identify the current SP based on the /alias/ part in the incoming requests.

                Problem with the audience you encountered is likely caused by the fact that the metadata you provided to your IDP is not correct.

                You can use the SAML Extension's sample app UI to generate your metadata including the configuration to be included in the Spring context.

                Cheers, Vladi

                Comment


                • #9
                  Thanks Vladi for your reply. In my situation, both the SP ids map to the same exact instance of my application. The reason i have to define 2 SPs and IDPs is due to an IDP restriction.
                  The restriction being, based on the kind of user login to the IDP (password or some sort of 2FA token authentication), the assertion sent to my application will reference a different SP ID and Sender will be different as well. So, it is the same 1 IDP sending me two different tokens based on the type of login.
                  I don't need to send my metadata to the IDP. I have their public Key and Cert information, out of which i created a IDP metadata document. Since login requests are always initiated by the IDP, my application just verifies the signature and validates the SAML token.

                  It seems like it might be easier to maintain one SP metadata document for both entity descriptors - instead of 2 different files(I have to also configure them for different environments Test, Prod etc.), also since
                  1) I don't really expose the metadata document to any body else and
                  2) since both SPs point to the same exact instance of my app
                  one metadata document might be a better approach?


                  Thanks again for all the great work!

                  Comment


                  • #10
                    Hello Everyone,

                    I'm aware that I'm posting a reply to an old thread, but I would like to get experts advice on the scenario and approach that I have taken for multi tenancy.

                    We have multiple IDP's (really different IDP hosted at our client side). We have sub-domain for each client, say xyz.mycompany.com (client 1)
                    abc.mycompany.com (client 2).

                    Both would have different IDP. What I have done is , created SP metadata for each of the IDP's and the SP metadata has HTTP-POST binding in it with AssertionConsumerURL having alias name say ../SSO/alias/xyz (for client 1 SP) and ../SSO/alias/abc (for client 2 SP). Both SP's use same certificate but the assertionconsumer URL is different.

                    I load both SP's with different alias name and at run time, when a client hits the url I'm changing the hostedSPName, setting selected IDP based on the site they are coming from.

                    My question is, is this the correct approach or is there something simple that I'm missing out.

                    Any thoughts or pointers would be much appreciated.

                    Thanks
                    Murali

                    Comment

                    Working...
                    X