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.