Traditionally, a Shibboleth IdP was configured with a login form asking for a username and password that get checked against an on-prem LDAP server (typically Active Directory). While that works and is easy to set up, it does not easily support MFA.
In the meantime, most Tuakiri members have adopted Entra ID and use it for accessing a number of applications. The security settings on Entra ID also allow configuring MFA (as required or optional) and MFA thus can become included in the authentication flow.
Shibboleth IdP (from version 4.2+) supports SAML proxying - and can authenticate against an upstream SAML IdP (acting as a SAML SP for the upstream authentication).
Also, as the upstream SAML IdP (Entra ID) can pass attributes alongside the authentication, the attribute resolver on the Tuakiri IdP can then be configured to construct user attributes based on what was received from Entra ID, instead of getting the attributes from a local LDAP server (Active Directory). This would eliminate the dependency of the IdP on the local LDAP server - but is an optional step and authentication can be moved to Entra ID without changing the local lookup.
While the general aspects of the configuration changes required are covered in the Shibboleth IdP documentation SAML proxying documentation, this page documents the specifics of configuring an IdP to authenticate against Entra ID (and also processing attributes if desired).
Note that SAML proxying to Entra ID is already used for all IdPs on the Tuakiri Hosted IdP service - this page makes the same configuration techniques available to Tuakiri members still unning their own IdP.
The IdP needs to be registered with Entra ID as a Custom SAML application.
These instructions are based on
Before starting the process, you will need:
example.org replaced by your organisations domain):
https://idp-test.example.org/idp/shibbolethhttps://idp.example.org/idp/shibbolethhttps://idp-test.example.org/idp/profile/Authn/SAML2/POST/SSOhttps://idp.example.org/idp/profile/Authn/SAML2/POST/SSOEntra ID / Azure has a number of popular service preconfigured - however, to add the SP side of a Shibboleth IdP instance, we will be adding a Custom SAML application.
Please repeat this process twice, separately for TEST and PROD registration.
givenName, surname, email, userprincipalname).
However, if migrating from authenticating against on-prem LDAP with a username/password, additional attributes may be needed:
userprincipalname (UPN) attribute is likely different from the username used with on-prem LDAP (UPN often takes the form fistname.lastname@example.org), to preserve the identifier attributes the users get when authenticating to services, it is crucial to also include an attribute corresponding to the on-prem username./opt/shibboleth-idp/conf/attribute-resolver.xml to see what attributes are being used.staff value of eduPersonAffiliation was derived from presence of a specific group name in the memberOf attribute, a suitable alternative may be to instead pass a boolean isStaff attribute, or pass staff value in a custom affiliation attribute. (Or completely construct eduPersonAffiliation in Entra ID).For easier handling, we recommend processing the received metadata with filter-idp-metadata.xslt stylesheet that:
ds:Signature element not needed for metadata trusted from a local filePlease download the filter-idp-metadata.xslt and use it with:
xsltproc -o metadata-filtered.xml filter-idp-metadata.xslt metadata-orig.xl
/opt/shibboleth-idp/metadata/EntraID_Test.xml (or EntraID_Prod.xml)/opt/shibboleth-idp/conf/metadata-providers.xml
<MetadataProvider id="UpstreamMetadata" xsi:type="FilesystemMetadataProvider" metadataFile="/opt/shibboleth-idp/metadata/EntraID_Test.xml"/>
/opt/shibboleth-idp/conf/authn/authn.properties (to value from the metadata downloaded after registering the applicaiton)
idp.authn.SAML.proxyEntityID = https://sts.windows.net/abcdefg0-1234-abcd-cdef-123456789abc/
Note For this setting to work, your IdP needs to be configured (in
/opt/shibboleth-idp/conf/idp.properties) to load the/opt/shibboleth-idp/conf/authn/auth.properiesfile - either explictly inidp.additionalPropertiesor viaidp.searchForProperties=true
/opt/shibboleth-idp/conf/attributes/custom/entraid_username.rule with the following content (assuming the username attribute will be called entraid_username in the IdP config and onpremiseusername in the SAML assertion sent by Entra ID):
id=entraid_username
transcoder=SAML2StringTranscoder
saml2.name=onpremiseusername
saml2.nameFormat=
saml2.encodeType=False
saml2.nameFormat value should be set to blank to match how Entra ID encodes attributes (to suppress the default format of urn:oasis:names:tc:SAML:2.0:attrname-format:uri not used by Entra ID)./opt/shibboleth-idp/conf/attribute-filter.xml and add (adjusting the Issuer value to match the entityID from your Entra ID metadata and attributeID to the name used in IdP config to represent the username sent by Entra ID):
<AttributeFilterPolicy id="proxy">
<PolicyRequirementRule xsi:type="Issuer" value="https://sts.windows.net/abcdefg0-1234-abcd-cdef-123456789abc/"/>
<AttributeRule attributeID="entraid_username" permitAny="true" />
</AttributeFilterPolicy>
/opt/shibboleth-idp/conf/c14n/subject-c14n.xml and uncomment the c14n/attribute flow./opt/shibboleth-idp/conf/c14n/subject-c14n.properties and set the following:
# Disable c14n-stage use of attribute-resolver
idp.c14n.attribute.resolutionCondition = shibboleth.Conditions.FALSE
# Enable getting attributes from Subject (without attribute resolver)
idp.c14n.attribute.resolveFromSubject = true
# Set source attribute ID
idp.c14n.attribute.attributeSourceIds = entraid_username
/opt/shibboleth-idp/conf/c14n/attribute-sourced-subject-c14n-config.xml (bean shibboleth.c14n.attribute.AttributeSourceIds)sameSite=none (to avoid issues where cookies would not be visible when being redirected back from Entra ID).
In /opt/shibboleth-idp/conf/idp.properties, set:
idp.cookie.sameSiteCondition = shibboleth.Conditions.TRUE
logout.vm, logout-propagate.vm, and logout-complete.vm templates in /opt/shibboleth-idp/views (into the body of each page after the main message). The text can e.g. be:
<p>
<br>
We also recommend logging out of your primary account to prevent
a future login from reusing the existing primary account session.
<br>
<a href="https://login.microsoftonline.com/logout.srf" target="_blank">
<strong>
Click here to log out of your primary account.
</strong>
</a>
</p>
/opt/shibboleth-idp/conf/idp.properties (or /opt/shibboleth-idp/conf/authn/auth.properies, depending on which IdP version your configuration files come from), change idp.authn.flows from Password to SAML::
idp.authn.flows = SAML
/opt/shibboleth-idp/conf/idp.properties and set:
idp.artifact.enabled = false
To prevent the IdP from exposing Entra ID as the AuthenticatingAuthority (which could disrupt how identity of users is seen in some services), edit /opt/shibboleth-idp/conf/relying-party.xml and in shibboleth.DefaultRelyingParty (and all beans under RelyingPartyOverrides beans), in the SAML2.SSO profile bean, add p:suppressAuthenticatingAuthority="true". (Note: requires IdP 4.2.0+)
http://schemas.microsoft.com/claims/multipleauthn) to the REFEDS MFA value https://refeds.org/profile/mfa accepted by R&E Identity Federations. Please follow the instructions in our guide for configuring REFEDS MFA on IdP proxing to Entra ID.Entra ID can pass claims (as SAML attributes) alongside the authentication and the attribute resolver on the Tuakiri IdP can then be configured to construct user attributes based on what was received from Entra ID. This allows eliminating the dependency of the IdP on the local LDAP.
To achieve this, Entra ID would have to provide input data for all attributes defined in /opt/shibboleth-idp/conf/attribute-resolver.xml:
attribute-resolver.xml file to see what attributes are being used and what their inputs are.staff value of eduPersonAffiliation was derived from presence of a specific group name in the memberOf attribute, a suitable alternative may be to instead pass a boolean isStaff attribute, or pass staff value in a custom affiliation attribute. (Or completely construct eduPersonAffiliation in Entra ID).http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname).On the Tuakiri IdP, for each IdP, repeat the steps already done above for the username attribute, using an internal attribute ID for both the rule name and the id value inside the rule, and the SAML2 for saml2.name:
/opt/shibboleth-idp/conf/attributes/custom/, e.g. /opt/shibboleth-idp/conf/attributes/custom/entraid_givenname.rule:
id=entraid_givenname
transcoder=SAML2StringTranscoder
saml2.name=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
saml2.nameFormat=
saml2.encodeType=False
Note It is important to avoid clashing with attribute names already defined on the IdP (such as
givenName) - therefore usingentraid_givennameto avoid the clash.
username attribute - e.g.:
<AttributeFilterPolicy id="proxy">
<PolicyRequirementRule xsi:type="Issuer" value="https://sts.windows.net/abcdefg0-1234-abcd-cdef-123456789abc/"/>
<AttributeRule attributeID="entraid_username" permitAny="true" />
<AttributeRule attributeID="entraid_givenname" permitAny="true" />
</AttributeFilterPolicy>
These have to be used instead of SimpleAttributeDefinition using an attribute value provided by a DataConnector.
For example:
<AttributeDefinition id="givenName" xsi:type="Simple" >
<InputDataConnector ref="myLDAP" attributeNames="givenName" />
...
becomes
<AttributeDefinition id="givenName" xsi:type="SubjectDerivedAttribute" principalAttributeName="entraid_givenname" >
...
As the received attributes are isolated from IdP attribute definitions, they need to be explictly imported (via an explicit IdPAttributeDefinition) before being used as inputs to other attribute definitions.
For example:
<AttributeDefinition id="eduPersonPrincipalName" xsi:type="Scoped" scope="example.org" >
<InputDataConnector ref="myLDAP" attributeNames="sAMAccountName" />
...
has to become
<AttributeDefinition id="username" xsi:type="SubjectDerivedAttribute" principalAttributeName="entraid_username" />
<AttributeDefinition id="eduPersonPrincipalName" xsi:type="Scoped" scope="example.org" >
<InputAttributeDefinition ref="username" />
...