Using SAML Proxying to another IdP

This how-to applies to Shibboleth Identity Provider v4.0 and can be used to integrate the authentication flows with other SAML2 compliant identity providers such as SimpleSAMLphp or Microsoft Azure. There is a far more detailed guide to integrating with Azure at Using SAML Proxying in the V4 Shibboleth IdP to connect with Azure AD.

Please read and follow the documentation first, before or along with using this example. This documentation is not maintained by the development team and may not be entirely accurate or consistent with the software at any given time. It is a complement to the documentation, not a replacement for it. It is currently out of date with respect to some improvements made in V4.1.

Delegating Authentication to another IdP

This process will absolve your Identity Provider (IdP) of the process of authenticating the user, instead relying on another SAML2 IdP to do that job. The resulting Assertion is used to generate the user's local username which is then used in the standard Attribute Resolution process.

In essence, your IdP well become a Service Provider (SP) to a second IdP.

This method describes extracting the username from an Attribute in the Assertion. It's also possible to use the Subject.

In this example, the following entities and attributes are in use:

  • Original IdP EntityID: https://idp.example.ac.uk/entity
  • Upstream IdP EntityID: https://upstream.idp/entity
  • Joining attribute (common to both services): uid (unscoped username)

Basic workflow

  1. Authentication request hits IdP
  2. IdP issues a redirect to Upstream IdP
  3. User authenticates (or reuses session?) against Upstream IdP and generates an Assertion
  4. Assertion consumed from upstream IdP
  5. Assertion filtered before use
  6. Attribute extracted from upstream Assertion for use as real username:
    1. Configure attribute-based subject c14n.
    2. Look through list of AttributesToResolve in the resolver and resolve each one.
    3. Look through list of resulting attributes in AttributeSourceIds and pick first valid one to be the principal's name to then be used later
  7. Resulting trusted real username used as $resolutionContext.principal (eg. existing LDAP data connector, etc.) during "standard" attribute resolution process

Pre-requisites

  1. A working Identity Provider at V4 or above
  2. A SAML2 compliant Identity Provider to delegate to
  3. A suitable attribute available to both IdPs to use as a "joining" attribute (so the other IdP can provide a value which can be looked up in the main IdP's connected data source)

Preparation

Update your IdP's metadata

As your IdP will need act as an SP, you'll need extra blocks in your entity's metadata. Create a new sp-metadata.xml (or update your existing idp-metadata.xml but consider whether this should be included in multi-lateral federation agreements) file to include a <SPSSODescriptor> block. You'll need to copy the signing and encryption certificates from the IdP part of the metadata and replace the base URI (https://idp.example.ac.uk/idp) with the base of your IdP.

<EntityDescriptor entityID="https://idp.example.ac.uk/entity" ...>

    <!-- New SP block -->
    <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">

        <KeyDescriptor use="signing">
            <ds:KeyInfo>
                <ds:X509Data>
                    <ds:X509Certificate>
                    ...Signing Certificate from IdP...
                    </ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </KeyDescriptor>
        <KeyDescriptor use="encryption">
            <ds:KeyInfo>
                <ds:X509Data>
                    <ds:X509Certificate>
                    ...Encryption Certificate from IdP...
                    </ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </KeyDescriptor>

        <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://idp.example.ac.uk/idp/profile/Authn/SAML2/POST/SSO" index="0"/>
    </SPSSODescriptor>

</EntityDescriptor>

Register your IdP with the upstream IdP

Use your updated metadata to register the IdP/SP with the IdP you'd like to delegate to and configure it to send a value to use an username (eg. eduPersonPrincipalName, uid or sAMAccountName).

You may just need to supply the EntityID and Assertion Consumer Service URL:

  • EntityID: same as for your IdP
  • Assertion Consumer Service: https://idp.example.ac.uk/idp/profile/Authn/SAML2/POST/SSO

Register the upstream IdP's metadata locally

If your upstream IdP is not already known by some other means, then register the upstream IdP, too. This is handled just like any other metadata and can be included in your metadata-providers.xml as a new <MetadataProvider> block. For example:

<MetadataProvider id="upstream-idp-metadata"
        xsi:type="FilesystemMetadataProvider"
        metadataFile="/opt/idp/metadata/upstream-idp.xml"
/>

IdP configuration

The following changes need to be made to the IdP to support this workflow:

  1. Enable the SAML authentication flow
  2. Configure to which Entity to delegate
  3. Update attribute filter to allow incoming attributes to be ingested
  4. Set up attribute extraction through Subject Canonicalisation (c14n and resolver)

Enable the SAML authentication flow

Edit authn/saml-authn-config.xml to set the EntityID of the upsteam IdP. Uncomment the shibboleth.authn.SAML.discoveryFunction bean and edit the target:

<bean id="shibboleth.authn.SAML.discoveryFunction" parent="shibboleth.Functions.Constant"
    c:target="https://upstream.idp/entity" />

Enable the flow by updating idp.authn.flows in idp.properties:

idp.authn.flows=SAML

Update Attribute Filter

The IdP will not ingest attributes from the Upstream IdP unless they're allowed in by a filter. Add a new <AttributeFilterPolicy> to permit trusted attributes to be handled:

<AttributeFilterPolicy id="saml-proxy-pass-through">
    <PolicyRequirementRule xsi:type="Issuer" value="https://upstream.idp/entity" />
    <AttributeRule attributeID="uid" permitAny="true" />
</AttributeFilterPolicy>

Subject Canonicalisation

This workflow takes the incoming assertion and extracts some data from it to work out the canonical (authoritative) username of the authenticated user. In this very simple case we're using uid (unscoped username).

Add a new <AttributeDefinition> to the attribute-resolver.xml which will transform the uid attribute from the Subject (the filtered data from the incoming assertion) into a proxied-uid attribute for use elsewhere:

<AttributeDefinition xsi:type="SubjectDerivedAttribute"
    forCanonicalization="true"
    principalAttributeName="uid"
    id="proxied-uid"
    />

With the Subject, configure which attributes to fire in the resolver to work out what's going on. Set AttributesToResolve in c14n/attribute-sourced-subject-c14n-config.xml to the id of the new <AttributeDefinition>:

<util:list id="shibboleth.c14n.attribute.AttributesToResolve">
    <value>proxied-uid</value>
</util:list>

This may need to include more attributes depending on how much work you need to do to turn the incoming attribute into the real username that you need later.

Set the AttributeSourceIds value to which newly resolved attributes you would treat as the real username. This is probably the same or a subset of AttributesToResolve.

Review c14n/subject-c14n.xml and ensure the PostLoginSubjectCanonicalizationFlow bean has been uncommented.

At this point you should have a valid locally understood username to pass into the "normal" resolver that already works, which is now available from places like:

$resolutionContext.principal

Test

Restart your IdP and test!

Your existing resolver should still work using aacli.

Related content