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/shibboleth
https://idp.example.org/idp/shibboleth
https://idp-test.example.org/idp/profile/Authn/SAML2/POST/SSO
https://idp.example.org/idp/profile/Authn/SAML2/POST/SSO
Entra 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.properies
file - either explictly inidp.additionalProperties
or 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_givenname
to 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" />
...