User authentication

The Shibboleth v4 IdP software natively supports four types of password authentication, being LDAP, JAAS, Kerberos and HTPasswd. For detailed configuration information please see the Shibboleth IdP v4 documentation pages:

Of the above, we expect more UK federation IdP operators to use Active Directory or another LDAP (Lightweight Directory Access Protocol) directory for user authentication and attribute generation, so we focus on that here.

Proxying with Azure Active Directory (AAD) is now a popular choice, and has been simplified by the introduction of a proxying module in Shibboleth IdP v4. This allows you to offload user authentication onto AAD and make use of its MFA (multi-factor authentication) services. We include here configuration guidance for authentication and attribute release with AAD; and for hybrid attribute release using a combination of AAD and Shibboleth's native LDAP support.

Active Directory and other LDAP directories

For detailed configuration information please see the Shibboleth IdP v4 documentation pages:

On Linux, to configure Shibboleth to use LDAP authentication (or one of the other three password login flows) you may need to enable the password authentication module as described in the Shibboleth IdP wiki. The Windows installer does this for you automatically.

You then configure the module in the %{idp.home}/conf/ldap.properties file as described in the Shibboleth IdP wiki and the following sections.

You will also need the CA or server certificate for the LDAP server, the baseDN to search for your users, and to know whether a subtree search is required.

Configuration stages

Active Directory: authentication

If using the Windows installer then some of the configuration in ldap.properties will have been done for you already, but some values are essentially guessed, and you will need to refine it further to fit your real Active Directory configuration. In Active Directory, the variable sAMAccountName is most commonly used to hold the username of someone authenticating at the IdP, so we assume that in the following.

Going through the ldap.properties file in order we look at the properties that you may need to change; any that we do not cover here will usually not need to be changed.

 ## Authenticator strategy, either anonSearchAuthenticator, bindSearchAuthenticator, directAuthenticator, adAuthenticator
 idp.authn.LDAP.authenticator=adAuthenticator

This will normally be adAuthenticator for Active Directory; the Windows Installer with the 'Use Active Directory' box ticked sets this for you.

 idp.authn.LDAP.ldapURL=ldaps://dc0.ad.example.ac.uk:636

This is the URL of your Active Directory server. The Windows installer makes a guess at this based on the Active Directory domain name entered at installation, and it will probably need to be changed. Check whether it is ldaps or ldap, and the hostname and port number, and change as necessary.

 idp.authn.LDAP.useStartTLS                     = false
 idp.authn.LDAP.useSSL                          = true

You should use a secure protocol, either startTLS or SSL, to connect to your Active Directory domain controller using LDAP. Modify the above properties according to which protocol you will use.

 ## SSL configuration, either jvmTrust, certificateTrust, or keyStoreTrust
 idp.authn.LDAP.sslConfig                       = certificateTrust
 ## If using certificateTrust above, set to the trusted certificate's path
 idp.authn.LDAP.trustCertificates=%{idp.home}/credentials/ldap-server.crt
 ## If using keyStoreTrust above, set to the truststore path
 # idp.authn.LDAP.trustStore=%{idp.home}/credentials/ldap-server.truststore

Then configure the public key certificate of the server(s) in %{idp.home}/credentials/ldap-server.crt, or alternatively modify the above configuration to use one of the other trust methods.

The default Active Directory configuration does not have a certificate installed for StartTLS or LDAP over SSL/TLS to be enabled, so many organisations may be using unencrypted LDAP on port 389. This is insecure.

We strongly recommended you configure your Active Directory service to support either StartTLS or SSL/TLS LDAP over SSL/TLS with a Certificate, and configure your Shibboleth IdP accordingly.

 #idp.authn.LDAP.subtreeSearch                   = true
 idp.authn.LDAP.userFilter=(sAMAccountName={user})
 # bind search configuration
 # for AD: idp.authn.LDAP.bindDN=adminuser@domain.com
 idp.authn.LDAP.bindDN=ldap-search@dc0.ad.example.ac.uk

The bindDN account is configured in the idp.authn.LDAP.bindDN property in %{idp.home}/conf/ldap.properties; the password for this account is configured in the %{idp.home}/credentials/secrets.properties file. Note that the bindDN must be in the form adminuser@domain.com when using the adAuthenticator strategy. Uncomment the idp.authn.LDAP.subtreeSearch line if you need subtree searching, and check that sAMAccountName is the correct variable name to hold the authenticating individual's username; if not then change accordingly.

 idp.attribute.resolver.LDAP.searchFilter= (sAMAccountName=$resolutionContext.principal)

Again, check that sAMAccountName is the correct variable name to hold the authenticating individual's username; if not then change accordingly.

Other LDAP directories: authentication

Other LDAP directories tend to have different naming conventions from Active Directory, so a number of the variable names in the configuration will be different from those in the Active Directory examples above. They will probably use the bindSearchAuthenticator strategy and thus require the service account to be specified in a different form.

Therefore, you will need to make similar changes to ldap.properties as set out for Active Directory above, but with some differences:

 ## Authenticator strategy, either anonSearchAuthenticator, bindSearchAuthenticator, directAuthenticator, adAuthenticator
 idp.authn.LDAP.authenticator=bindSearchAuthenticator

Specify bindSearchAuthenticator for the authenticator strategy.

 # Search DN resolution, used by anonSearchAuthenticator, bindSearchAuthenticator
 # for AD: CN=Users,DC=example,DC=org
 idp.authn.LDAP.baseDN=ou=users,DC=ad,DC=example,DC=ac,DC=uk
 #idp.authn.LDAP.subtreeSearch                   = true
 idp.authn.LDAP.userFilter=(uid={user})
 # bind search configuration
 # for AD: idp.authn.LDAP.bindDN=adminuser@domain.com
 idp.authn.LDAP.bindDN=uid=ldap-search,ou=users,dc=ad,dc=example,dc=ac,dc=uk

The bindDN account is configured in the idp.authn.LDAP.bindDN property in %{idp.home}/conf/ldap.properties; the password for this account is configured in the %{idp.home}/credentials/secrets.properties file. Note that the bindDN is in a different form when using the bindSearchAuthenticator strategy from the form used with adAuthenticator. Uncomment the idp.authn.LDAP.subtreeSearch line if you need subtree searching, and check that uid is the correct variable name to hold the authenticating individual's username; if not then change accordingly.

 idp.attribute.resolver.LDAP.searchFilter= (uid=$resolutionContext.principal)

Again, check that uid is the correct variable name to hold the authenticating individual's username; if not then change accordingly.

As with Active Directory, you should always be using a secure protocol to connect to any LDAP directory service.

We strongly recommended you configure your LDAP directory service to support either StartTLS or SSL/TLS LDAP over SSL/TLS with a Certificate, and configure your Shibboleth IdP accordingly.

Active Directory and other LDAP Directories: attribute release

The IdP releases SAML attributes to SPs to provide information about the organisation and user that the IdP represents for a given authentication. Some are privacy-preserving and some are not, so your organisation needs to give consideration to which attributes are released to which SPs. For the UK federation's attribute release recommendations generally, please see section 7 of the UK federation's Technical Recommendations for Participants. There are four attributes that the UK federation regards as "core" attributes: eduPersonScopedAffiliation, eduPersonTargetedID, eduPersonPrincipalName and eduPersonEntitlement. The most commonly used of these are eduPersonScopedAffiliation and eduPersonTargetedID, so we focus on those here.

Each attribute must have a definition in the attribute-resolver.xml file, which gives instructions for generating the attribute value and format; and a filter in the attribute-filter.xml file, which gives instructions on which SPs it can be released to and under which circumstances.

The Attribute Registry service is a new addition to V4 that provides a more advanced way to configure the relationship between the internal IdPAttribute objects that are (for the most part) produced by the Attribute Resolver and the way the data is represented in the protocols supported by software like SAML, CAS, or OpenID.

For the details of this please see the Shibboleth link above; but one consequence of the introduction of the Attribute Registry service is that AttributeEncoder elements are no longer required in attribute definitions in most cases. The documentation here is intended for setting up new deployments so we provide attribute definitions here without AttributeEncoder elements. If you are migrating an older deployment we recommend you update your attribute definitions accordingly; the inclusion of AttributeEncoder elements may resulting in duplicate attribute values being released, which might result in issues with some SPs.

Attribute resolver configuration examples

Details of the Shibboleth IdP attribute resolver configuration are given in the Shibboleth attribute resolver configuration documentation. The attribute resolver configuration specifies how attributes are retrieved or generated on behalf of your users; the conf/attribute-resolver.xml file is used for this.

Some simple attribute definitions follow, with an example LDAP connector and ComputedId connector. These are based on examples in the file %{idp.home}/conf/examples/attribute-resolver-ldap.xml. Your attribute definitions will depend on which attributes are available in your LDAP.

Copy the file %{idp.home}/conf/examples/attribute-resolver-ldap.xml to %{idp.home}/conf/attribute-resolver.xml and use that as a starting point. You will need to make some changes according to your organisation's local set-up.

 <AttributeResolver
        xmlns="urn:mace:shibboleth:2.0:resolver"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver
        http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">

     <AttributeDefinition xsi:type="Scoped" id="eduPersonPrincipalName" scope="%{idp.scope}">
         <InputDataConnector ref="myLDAP" attributeNames="sAMAccountName"/>
     </AttributeDefinition>

     <AttributeDefinition xsi:type="Scoped" id="eduPersonScopedAffiliation" scope="%{idp.scope}">
         <InputDataConnector ref="myLDAP" attributeNames="eduPersonAffiliation"/>
     </AttributeDefinition>

     <AttributeDefinition xsi:type="Scoped" id="samlPairwiseID" scope="%{idp.scope}">
         <InputDataConnector ref="computed" attributeNames="computedId"/>
     </AttributeDefinition>

     <AttributeDefinition xsi:type="Scoped" id="samlSubjectID" scope="%{idp.scope}">
         <InputDataConnector ref="myLDAP" attributeNames="%{idp.persistentId.sourceAttribute}"/>
     </AttributeDefinition>

     <!-- Deprecated eduPersonTargetedID attribute -->

     <AttributeDefinition xsi:type="SAML2NameID" id="eduPersonTargetedID"
         nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
         <InputDataConnector ref="computed" attributeNames="computedId"/>
         <AttributeEncoder xsi:type="SAML1XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10"/>
         <AttributeEncoder xsi:type="SAML2XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10"
                           friendlyName="eduPersonTargetedID"/>
     </AttributeDefinition>

     <!-- Example LDAP DataConnector based on conf/examples/attribute-resolver-ldap.xml -->

     <DataConnector id="myLDAP" xsi:type="LDAPDirectory"
         ldapURL="%{idp.attribute.resolver.LDAP.ldapURL}"
         baseDN="%{idp.attribute.resolver.LDAP.baseDN}"
         principal="%{idp.attribute.resolver.LDAP.bindDN}"
         principalCredential="%{idp.attribute.resolver.LDAP.bindDNCredential}"
         useStartTLS="%{idp.attribute.resolver.LDAP.useStartTLS:true}"
         connectTimeout="%{idp.attribute.resolver.LDAP.connectTimeout}"
         trustFile="%{idp.attribute.resolver.LDAP.trustCertificates}"
         responseTimeout="%{idp.attribute.resolver.LDAP.responseTimeout}"
         connectionStrategy="%{idp.attribute.resolver.LDAP.connectionStrategy}"
         noResultIsError="true"
         multipleResultsIsError="true"
         excludeResolutionPhases="c14n/attribute"
         exportAttributes="sAMAccountName eduPersonAffiliation">
         <FilterTemplate>
             <![CDATA[
                 %{idp.attribute.resolver.LDAP.searchFilter}
             ]]>
         </FilterTemplate>
         <ConnectionPool
             minPoolSize="%{idp.pool.LDAP.minSize:3}"
             maxPoolSize="%{idp.pool.LDAP.maxSize:10}"
             blockWaitTime="%{idp.pool.LDAP.blockWaitTime:PT3S}"
             validatePeriodically="%{idp.pool.LDAP.validatePeriodically:true}"
             validateTimerPeriod="%{idp.pool.LDAP.validatePeriod:PT5M}"
             validateDN="%{idp.pool.LDAP.validateDN:}"
             validateFilter="%{idp.pool.LDAP.validateFilter:(objectClass=*)}"
             expirationTime="%{idp.pool.LDAP.idleTime:PT10M}"/>
     </DataConnector>

     <!--
     DataConnector for pairwise-id (example depends in part on saml-nameid.properties).
     Note that this relies on BASE32 encoding in accordance with the attribute definition.
     Older uses of this plugin for legacy eduPersonTargetedID/NameID values may require
     different settings.
     -->

     <DataConnector id="computed" xsi:type="ComputedId"
             excludeResolutionPhases="c14n/attribute"
             generatedAttributeID="computedId"
             salt="%{idp.persistentId.salt}"
             algorithm="%{idp.persistentId.algorithm:SHA}"
             encoding="BASE32">
         <InputDataConnector ref="myLDAP" attributeNames="%{idp.persistentId.sourceAttribute}" />
     </DataConnector>

 </AttributeResolver>

The example above gives attribute definitions for the SAML attributes eduPersonPrincipalName eduPersonScopedAffiliation, samlPairwiseID, samlSubjectID and eduPersonTargetedID. The attribute definitions depend on the DataConnector with id="myLDAP". If your LDAP connection is secured with SSL or StartTLS then you should not need to make any changes; if it is not secured then you may need to remove or comment out the following lines from the myLDAP DataConnector:

 useStartTLS="%{idp.attribute.resolver.LDAP.useStartTLS:true}"
 trustFile="%{idp.attribute.resolver.LDAP.trustCertificates}"

We strongly recommend you configure your LDAP Server to support either StartTLS or SSL/TLS LDAP over SSL/TLS with a Certificate, and re-configure your Shibboleth IdP accordingly.

You will probably need to make changes to the following line:

 exportAttributes="sAMAccountName eduPersonAffiliation">

In our example, the sAMAccountName and eduPersonAffiliation directory attributes are retrieved from the LDAP directory to be used as the basis for building your SAML attributes. You may need to change these, and/or add others to the list, depending on which attributes are stored in your LDAP directory and what your SAML attribute requirements are. sAMAccountName (the username of the authenticating user) is used in this example as a source attribute for the SAML attribute eduPersonPrincipalName. eduPersonAffiliation, a multi-valued directory attribute containing one or more user affiliations, such as (staff, member) or (student, member), is used in this example as a source attribute for the SAML attribute eduPersonScopedAffiliation.

eduPersonScopedAffiliation

Assuming you have a DataConnector configuration with id="myLDAP", and the LDAP has been populated with the eduPersonAffiliation attribute for all your users, then you can use the eduPersonAffiliation attribute as the basis to generate eduPersonScopedAffiliation with the following snippet in attribute-resolver.xml:

 <AttributeDefinition xsi:type="Scoped" id="eduPersonScopedAffiliation" scope="%{idp.scope}">
     <InputDataConnector ref="myLDAP" attributeNames="eduPersonAffiliation"/>
 </AttributeDefinition>

If the LDAP has not been populated with eduPersonAffiliation and it is not possible to arrange for it to be populated then you may need to use an alternative attribute definition for eduPersonAffiliation, based on other attributes or groups in the LDAP directory.

eduPersonTargetedID

This attribute again depends on having a DataConnector configuration to connect to your LDAP. It also requires a ComputedId DataConnector. See the DataConnector with id="computed" in the example above.

You should not need to make any changes to that; but you will need to make some changes to the file saml-nameid.properties. In saml-nameid.properties, set the idp.persistentId.sourceAttribute be the name of a directory attribute suitable for use as a source attribute for eduPersonTargetedID and other persistent SAML attributes. Whichever source attribute is chosen, it should not change over time. Commonly the username is used; in OpenLDAP this is often called uid.

 idp.persistentId.sourceAttribute=uid

In Active Directory, the username is often called sAMAccountName, although this has been known to change during the lifetime of a user's account. Depending on your system, either objectGUID or objectSid could be used.

The objectGUID is binary, so if it is to be used as the source attribute it should be declared as binary by adding this line to the LDAP DataConnector with id="myLDAP":

 <LDAPProperty name="java.naming.ldap.attributes.binary" value="objectGUID"/>

If you use objectSid, you'll need to add a similar element to the LDAP DataConnector.

You also need to set the property idp.persistentId.salt in %{idp.home}/credentials/secrets.properties. It is a random string of at least 16 bytes which you choose. It should be kept secret.

 idp.persistentId.salt=0123456789abcdef

Important: if you are migrating from an earlier deployment you should use the same source attribute and salt value as you used previously, and set idp.persistentId.encoding = BASE64 in saml-nameid.properties, to ensure that the same values are generated for each of your users as were previously. This is necessary to retain access to personalisations saved on SPs that are keyed to eduPersonTargetedID values.

 idp.persistentId.encoding = BASE64

And here then is the configuration snippet in attribute-resolver.xml for eduPersonTargetedID:

 <AttributeDefinition xsi:type="SAML2NameID" id="eduPersonTargetedID"
     nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
     <InputDataConnector ref="computed" attributeNames="computedId"/>
     <AttributeEncoder xsi:type="SAML1XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10"/>
     <AttributeEncoder xsi:type="SAML2XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10"
                       friendlyName="eduPersonTargetedID"/>
 </AttributeDefinition>
Attribute filter configuration

Details of the Shibboleth IdP attribute filter configuration are given in the Shibboleth attribute filter configuration documentation. The attribute filter configuration determines which attributes are released to which service providers, and is configured in the conf/attribute-filter.xml file. Most service providers in the UK federation require the attribute eduPersonScopedAffiliation with an affiliation value equivalent to full membership, eg. member, staff or student. The affiliation values released for a given user must be determined on the basis of the user's genuine affiliation with your organisation from your organisation's point of view.

In general the UK federation considers there to be very little personally identifying information carried by either eduPersonScopedAffiliation or eduPersonTargetedID so considers it safe to release them to any SP; the following configuration snippet does just that. However, your organisation should do its own risk analysis in consultation with its data protection officer rather than taking our word for it.

Note that this snippet would release these two attributes to any SP with which the IdP successfully authenticated; this might be a UK federation SP, or an SP imported from another federation via eduGAIN interfederation, or another SP for which the IdP has the metadata configured one way or another. More sophisticated attribute filters are possible; please see the Shibboleth documentation.

This simple example releases eduPersonScopedAffiliation and eduPersonTargetedID to any SP.

 <AttributeFilterPolicyGroup id="ShibbolethFilterPolicy"
        xmlns="urn:mace:shibboleth:2.0:afp"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="urn:mace:shibboleth:2.0:afp
        http://shibboleth.net/schema/idp/shibboleth-afp.xsd">

    <!-- Release eduPersonScopedAffiliation and eduPersonTargetedID attributes to any SP. -->
    <AttributeFilterPolicy id="releaseToAnyone">
        <PolicyRequirementRule xsi:type="ANY" />
        <AttributeRule attributeID="eduPersonScopedAffiliation" permitAny="true" />
        <AttributeRule attributeID="eduPersonTargetedID" permitAny="true" />
    </AttributeFilterPolicy>

 </AttributeFilterPolicyGroup>

Azure Active Directory

For detailed configuration information please see the Shibboleth IdP v4 documentation page:

Please also see this "how-to" guide on the Shibboleth wiki for information about configuring Azure as a user authentication and attribute source. Note that the guide is out of date with respect to changes from Shibboleth IdP v4.1 onwards. In this section we provide advice that incorporates those changes and is up to date as of v4.3.

Configuration stages

We provide here guidance for configuring Azure AD as an authentication source for the Shibboleth IdP, and also as an attribute source. When AAD is the authentication source and the sole attribute source then we refer to it as pass-through proxying.

We also provide guidance for configuring Azure AD as authentication source and (possibly) attribute source in combination with Active Directory (or other LDAP directory) as attribute source; this we refer to as hybrid proxying.

When hybrid proxying we choose a single attribute from AAD which corresponds to (and typically would be synchronised with) an attribute in Active Directory/other LDAP directory. We call this the join attribute; we suggest using user.onpremisessamaccountname as the join attribute, and have done so in the examples here.

If you plan to use Active Directory (or other LDAP directory) as an attribute source then it may be easier to configure the IdP to authenticate and resolve attributes via LDAP first, according to our Active Directory/LDAP configuration guidance, then switch to hybrid proxying using the configuration stages below.

Configure Trust between AAD and Shibboleth IdP

These first three stages involve registering the Shibboleth IdP with Azure AD and providing the Shibboleth IdP and Azure AD with each other's metadata.

Register Shibboleth IdP with AAD
  • Sign into the Azure Active Directory portal
  • Click Azure Active Directory
  • Click Enterprise applications
  • Click New application
  • Click Create your own application
  • Choose the "Integrate any other application you don't find in the gallery (Non-gallery)" radio button
  • Type in a name for your IdP eg. Shibboleth IdP
  • Click Create

You should then see an overview of the application with several options under the "Getting Started" heading.

  • In the "Set up single sign on" option click "Get started"
  • Under the "Select a single sign-on method" heading click SAML
  • Under "Set up Single Sign-On with SAML" click "Edit" in "Basic SAML Configuration"
  • Under "Basic SAML Configuration" click "Add identifier". Copy your Shibboleth IdP entityID into the box and click Add identifier again
  • Still under "Basic SAML Configuration" click "Add reply URL". Paste your IdP's <AssertionConsumerService> URL as created above into the box
  • Click Save
  • Click the cross at top right of the page to exit Basic SAML Configuration, and keep the SAML-based Sign-on page open for the next step
Edit Shibboleth IdP metadata provider configuration

Still in Azure, under "SAML Certificates" next to "Federation Metadata XML" click "Download" to download the Azure IdP metadata. The file will be easier to edit if you use an XML editor to format or "pretty print" the metadata. Edit the metadata file. You can remove some unnecessary elements to reduce the size of the file and make it easier to edit and understand; the unnecessary elements are:

  • the Signature element at the top of the file
  • the Fed:ClaimTypesOffered section

Then make additions as follows:

  • Add this namespace to the EntityDescriptor: xmlns:shibmd="urn:mace:shibboleth:metadata:1.0"
  • Add an <Extensions> block inside the IDPSSODescriptor containing a Scope element with your Shibboleth IdP's scope, eg.
 <Extensions>
   <shibmd:Scope regexp="false">example.ac.uk</shibmd:Scope>
 </Extensions>

Save the file and copy it to to the Shibboleth IdP's %{idp.home}/metadata/ directory; name the file AAD.xml.

Edit the Shibboleth IdP's %{idp.home}/conf/metadata-providers.xml file and add an entry for the Azure IdP metadata, eg:

 <!-- AD.xml - local metadata file for test Azure Active Directory -->  
 <MetadataProvider id="LocalMetadata" xsi:type="FilesystemMetadataProvider"
                   metadataFile="%{idp.home}/metadata/AAD.xml"/>

Note about Azure AD certificates: the Azure AD certificate has a three-year lifetime, meaning that the IdP administrators need to handle Azure AD certificate rollover every three years shortly before the certificate is due to expire to maintain trust between Shibboleth and AAD.

Edit Shibboleth IdP metadata file
  • Edit the %{idp.home}/metadata/idp-metadata.xml file to include an <SPSSODescriptor> block
  • Copy the <KeyDescriptor> elements containing the signing and encryption certificates from the IdP part of the metadata into the <SPSSODescriptor> block
  • Add an <AssertionConsumerService> URL immediately before the closing </SPSSODescriptor> tag of the form https://idp.example.ac.uk/idp/profile/Authn/SAML2/POST/SSO, replacing idp.example.ac.uk with your IdP hostname

Change IdP authentication flow to SAML

In this step we change the IdP's authentication flow to SAML, to enable it to proxy with Azure AD; and provide the IdP with the Azure AD entityID.

  • Edit the Shibboleth IdP's %{idp.home}/conf/authn/authn.properties file
  • change the value of idp.authn.flows to SAML:
 idp.authn.flows=SAML
  • set the value of idp.authn.SAML.proxyEntityID to the AAD IdP entityID as found in the AAD IdP metadata, eg:
 idp.authn.SAML.proxyEntityID=https://sts.windows.net/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx/

Test authentication

At this point users configured in AAD should be able to authenticate with the IdP using AAD as an authentication proxy. Test authentication using the UK federation test SP. You don't expect attributes to be released at this stage, but users should be authenticated and redirected to the test SP page. Make sure this is working before moving on the next section.

Information about testing using the UK federation test SP is in the Testing IdP deployments section.

Configure AAD attribute release to the Shibboleth IdP

As mentioned previously, if all the attributes we wish to release to service providers are stored in AAD then we call it pass-through proxying. If some or all of the attributes we wish to release are stored in Active Directory or other LDAP directory then we call it hybrid proxying. We might combine the two approaches and release attributes from both AAD and Active Directory/other directory.

Pass-through proxy

All attributes originate from Azure AD and must be released to the Shibboleth IdP, which will transform them and release to the SP. Configure attribute release in AAD as follows:

  • In Azure AD click "Enterprise application"
  • Click the name of the enterprise application you created previously, eg. "Shibboleth IdP"
  • Under "Set up single sign on" click "Get started"
  • Click "Edit" on "Attributes & Claims"
  • Configure release of all the attributes you want to release to SPs
Hybrid proxy

One attribute is needed from AAD which can then be used for user lookup in Active Directory/other LDAP directory: the join attribute. We suggest using user.onpremisessamaccountname as the join attribute. However if a combined approach, ie. releasing attributes from both AAD and a directory, is being taken then more attributes may be released from AAD as above. Configure release of the join attribute in AAD as follows:

  • In Azure AD click "Enterprise application"
  • Click the name of the enterprise application you created previously, eg. "Shibboleth IdP"
  • Under "Set up single sign on" click "Get started"
  • Click "Edit" on "Attributes & Claims"
  • Configure release of user.onpremisessamaccountname or other attribute designated as join attribute
Update IdP attribute filter

You need to update the IdP attribute filter to permit attributes to be released by Azure AD to the IdP. Edit %{idp.home}/conf/attribute-filter.xml and add a suitable AttributeFilterPolicy rule. You can base it on this example, setting value to the AAD IdP entityID as found in the AAD IdP metadata:

 <AttributeFilterPolicy id="FilterPolicyObject-Proxy-FromAzure-byIssuer-Type">
    <PolicyRequirementRule xsi:type="Issuer"
        value="https://sts.windows.net/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx/" />

    <AttributeRule attributeID="azureDisplayname" permitAny="true" />
    <AttributeRule attributeID="azureGivenname" permitAny="true" />
    <AttributeRule attributeID="azureSurname" permitAny="true" />
    <AttributeRule attributeID="azureAuthnmethodsreferences" permitAny="true" />
    <AttributeRule attributeID="azureIdentityprovider" permitAny="true" />
    <AttributeRule attributeID="azureTenantid" permitAny="true" />
    <AttributeRule attributeID="azureEmailaddress" permitAny="true" />
    <AttributeRule attributeID="azureObjectidentifier" permitAny="true" />
    <AttributeRule attributeID="azureName">
        <PermitValueRule xsi:type="ScopeMatchesShibMDScope" />
    </AttributeRule>
    <AttributeRule attributeID="azureOnPremisessAMAccountName" permitAny="true" />
 </AttributeFilterPolicy>

You probably don't need all the attributes above, and you may want to use others not listed above. If you plan to base some or all of your SAML attributes for release to service providers on attributes in Azure AD then you will need to include all the Azure AD attributes necessary for that in your attribute filter.

Note that the ScopeMatchesShibMDScope rule above for azureName is based on the assumption that your Azure AD uses the same scope as your Shibboleth IdP. If it does not then you will need to use another rule, such as permitAny="true".

If you plan to base all your SAML attributes for release to service providers on the attributes in Active Directory (or other LDAP directory) then you only need a single attribute from Azure AD to use as the "join" attribute to retrieve attributes from the directory. You can use the example below to do this, based on our recommendation of user.onpremisessamaccountname as the join attribute. Set value to the AAD IdP entityID as found in the AAD IdP metadata.

 <AttributeFilterPolicy id="FilterPolicyObject-Proxy-FromAzure-byIssuer-Type">
    <PolicyRequirementRule xsi:type="Issuer"
        value="https://sts.windows.net/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx/" />

    <AttributeRule attributeID="azureOnPremisessAMAccountName" permitAny="true" />
 </AttributeFilterPolicy>
Configure IdP to recognise AAD claims
Add attribute mapping file

Create a file in %{idp.home}/conf/attributes called azureClaims.xml. This file includes all of the attributes from Azure AD that you need to use in the Shibboleth IdP attribute resolver. The example below is not exhaustive. We need to add sAMAccountName to this.

There needs to be a transcoder bean for each attribute that is being retrieved from Azure AD, whether it is to be used just as a join attribute for retrieving attributes from directory, or as the basis of an attribute definition in attribute-resolver.xml.

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"

       default-init-method="initialize"
       default-destroy-method="destroy">

    <bean parent="shibboleth.TranscodingRuleLoader">
    <constructor-arg>
    <list>
        <!-- claims relevant to person record -->
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureOnPremisessAMAccountName</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/samaccountname</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">onPremises sAMAccountName</prop>
                    <prop key="description.en">Azure on-premises sAMAccountName</prop>
                </props>
            </property>
        </bean>
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureName</prop>
                    <prop key="transcoder">SAML2ScopedStringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">Name</prop>
                    <prop key="description.en">Azure UPN of an account expected to be scoped thus transcoded that way</prop>
                </props>
            </property>
        </bean>
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureEmailaddress</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">Mail</prop>
                    <prop key="description.en">Azure emailaddress field of an account</prop>
                </props>
            </property>
        </bean>
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureDisplayname</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.microsoft.com/identity/claims/displayname</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">Mail</prop>
                    <prop key="description.en">Azure displayname field of an account</prop>
                </props>
            </property>
        </bean>
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureGivenname</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">Given name</prop>
                    <prop key="description.en">Azure given name of an account</prop>
                </props>
            </property>
        </bean>
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureSurname</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">Surname</prop>
                    <prop key="description.en">Azure surname of an account</prop>
                </props>
            </property>
        </bean>

        <!-- default claims from Azure for any entity -->
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureTenantid</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.microsoft.com/identity/claims/tenantid</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">Tenant ID</prop>
                    <prop key="description.en">Azure tenantid</prop>
                </props>
            </property>
        </bean>
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureObjectidentifier</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.microsoft.com/identity/claims/objectidentifier</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">Object Identifier</prop>
                    <prop key="description.en">Azure object identifier of an account</prop>
                </props>
            </property>
        </bean>
         <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureIdentityprovider</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.microsoft.com/identity/claims/identityprovider</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">Identity Provider entityid</prop>
                    <prop key="description.en">Azure entityID of the tenant in Azure</prop>
                </props>
            </property>
        </bean>
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureAuthnmethodsreferences</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.microsoft.com/claims/authnmethodsreferences</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">Authnmethodsreferences</prop>
                    <prop key="description.en">Azure authentication method (password, modern auth? etc)</prop>
                </props>
            </property>
        </bean>

    </list>
    </constructor-arg>
    </bean>

 </beans>

If you only plan to release attributes from Active Directory/other LDAP directory then you only need to include the join attribute in the attribute mapping file, for example:

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"

       default-init-method="initialize"
       default-destroy-method="destroy">

    <bean parent="shibboleth.TranscodingRuleLoader">
    <constructor-arg>
    <list>
        <!-- claims relevant to person record -->
        <bean parent="shibboleth.TranscodingProperties">
            <property name="properties">
                <props merge="true">
                    <prop key="id">azureOnPremisessAMAccountName</prop>
                    <prop key="transcoder">SAML2StringTranscoder</prop>
                    <prop key="saml2.name">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/samaccountname</prop>
                    <prop key="saml2.nameFormat">urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified</prop>
                    <prop key="displayName.en">onPremises sAMAccountName</prop>
                    <prop key="description.en">Azure on-premises sAMAccountName</prop>
                </props>
            </property>
        </bean>

    </list>
    </constructor-arg>
    </bean>

 </beans>
Add reference to attribute mapping file

Update %{idp.home}/conf/attributes/default-rules.xml to refer to the new attribute mapping file by adding this line after the last import:

 <import resource="azureClaims.xml" />

Release attributes from AAD

Follow the steps in this section to release attributes that are stored in Azure AD.

Update IdP attribute resolver

Add a data connector to %{idp.home}/conf/attribute-resolver.xml to reveal the attributes from Azure AD as follows:

 <DataConnector id="passthroughAttributes" xsi:type="Subject"
     exportAttributes="azureName azureEmailaddress azureDisplayname azureGivenname
         azureSurname azureTenantid azureObjectidentifier azureIdentityprovider
         azureAuthnmethodsreferences" />

You only need to include the attributes stored in Azure AD that you will be releasing to SPs. If you are using a join attribute to retrieve attributes from Active Directory/other LDAP directory then you do not need to include it. If you are only releasing attributes that are stored in Active Directory/other LDAP directory then you do not need the above passthroughAttributes DataConnector at all.

Add attribute definitions as follows. Notice that the value of principalAttributeName will be one of the exported attributes from the above DataConnector:

 <AttributeDefinition xsi:type="SubjectDerivedAttribute"
     forCanonicalization="false"
     id="mail"
     principalAttributeName="azureEmailaddress" />

 <AttributeDefinition xsi:type="SubjectDerivedAttribute"
     forCanonicalization="false"
     id="displayName"
     principalAttributeName="azureDisplayname" />

 <AttributeDefinition xsi:type="SubjectDerivedAttribute"
     id="eduPersonPrincipalName"
     principalAttributeName="azureName" />

Release attributes from directory

Follow the steps in this section to release attributes stored in Active Directory, or other LDAP directory. As has been discussed previously, you need to select an attribute in AAD that corresponds to an attribute in the LDAP directory and acts as a username to use as your join attribute. We are using user.onpremisessamaccountname as the "join" attribute in our examples here.

Configure join attribute and subject canonicalisation

To configure the join attribute, edit %{idp.home}/conf/c14n/subject-c14n.properties, putting the attribute being used for join in the idp.c14n.attribute.attributeSourceIds property, eg.

 idp.c14n.attribute.attributeSourceIds = azureOnPremisessAMAccountName
 # Allows direct use of attributes via SAML proxy authn, bypasses resolver
 idp.c14n.attribute.resolveFromSubject = true
 idp.c14n.attribute.resolutionCondition = shibboleth.Conditions.FALSE

We need to perform subject canonicalisation on the "join" attribute. Edit %{idp.home}/conf/c14n/subject-c14n.xml and uncomment this bean:

      <!-- Remove comment tags to enable Attribute-based c14n -->
      <bean id="c14n/attribute" parent="shibboleth.PostLoginSubjectCanonicalizationFlow" />
Update IdP attribute resolver

To configure attribute resolution for attributes stored in Active Directory/other LDAP directory, please refer to our Active Directory and other LDAP directories section. You will need to configure authentication and attribute release as described in that section.

Update IdP attribute filter

To configure attribute filtering, ie. to determine which attributes are released to which SPs, please refer to our Active Directory and other LDAP directories attribute filter configuration section, for which configuration is the same.