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 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. |
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:
https://idp.example.ac.uk/entity
https://upstream.idp/entity
uid
(unscoped username)AttributesToResolve
in the resolver and resolve each one.
AttributeSourceIds
and pick first valid one to be the principal's name to then be used later$resolutionContext.principal
(eg. existing LDAP data connector, etc.) during "standard" attribute resolution processAs 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> |
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:
https://idp.example.ac.uk/idp/profile/Authn/SAML2/POST/SSO
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" /> |
The following changes need to be made to the IdP to support this workflow:
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
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> |
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
Restart your IdP and test!
Your existing resolver should still work using aacli
.