Sitecore Identity Server: Mapping Custom Fields and Roles from Azure AD

Sitecore Own the experience

Today I needed to setup a Sitecore 9.3 Instance to only be accessible by users logging in from Azure AD. Additionally, we needed an additional field UPN and for roles to be assigned based on azure security group membership. There was quite a mixture of config changes in a different places, so i'm writing this article as a reminder for myself and also anyone else who might find it useful.

This article will look at:

  • Create default user profile (to store UPN)
  • Setup a new security domain in Sitecore
  • Control access to site node using domain
  • Configure authentication per site node
  • Create an Azure AD Application Group
  • Setup Identity Server to use Azure AD and pass claims

Create default user profile

In order to be able to store additional information retrieved from the Identity Provider, we need to create a custom user profile data template in Sitecore.

  1. Go to desktop → Core Database
  2. Content Editor
  3. Create new template (based on User template) i.e. /sitecore/templates/System/Security/Azure User
  4. Add field UPN

Alternatively you can edit the default template here: /sitecore/templates/System/Security/User

Setup a new security domain in Sitecore

Edit the file: App_Config/Security/Domains.config

Add a new node for the custom domain:

<domain 
    name="azure" 
    ensureAnonymousUser="false" 
    anonymousUserName="" 
    everyoneRoleName=""
    defaultProfileItemID="{2604E795-BD17-4B9B-9E1F-18413C0B1E0C}" />

Setting anonymousUserName and everyoneRoleName to blank to disable the anonymous/everyone user for the domain. defaultProfileItemIDspecifies what profile template to use for new users (i.e. one that now contains UPN).

Control access to site node using domain

In order to ensure only users from our new domain can access the site, we need to edit the sites node to specify our new domain and ensure requireLogin is set to true.

      <site name="mysitename"
            hostname="myhostname"
            enableTracking="true"
            virtualFolder="/"
            physicalFolder="/"
            rootPath="/sitecore/content/mysitename"
            startItem="/Home"
            database="web"
            domain="azure"
            allowDebug="true"
            cacheHtml="true"
            htmlCacheSize="150MB"
            registryCacheSize="0"
            viewStateCacheSize="0"
            xslCacheSize="25MB"
            filteredItemsCacheSize="10MB"
            enablePreview="true"
            enableWebEdit="true"
            enableDebugger="true"
            disableClientData="false"
            cacheRenderingParameters="true"
            renderingParametersCacheSize="10MB"
	    requireLogin="true"
            patch:before="*[@name='website']"/>

Configure authentication per site node

The main identity server configuration within sitecore found here: \App_Config\Sitecore\Owin.Authentication.IdentityServer\Sitecore.Owin.Authentication.IdentityServer.config

Note: the setting identityServerAuthority should already have been set to point to the new identity server URL (i.e. https://identity-mysitename.uk. If not, this is the main area to configure to connect the Sitecore to Identity Server.

  1. Add / Uncomment the following, to identify the Azure AD sub provider:
    <identityProvider id="SitecoreIdentityServer/IdS4-AzureAd" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication">
      <param desc="name">$(id)</param>
      <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
      <caption>Log in with Sitecore Identity: Azure AD</caption>
      <icon>/sitecore/shell/themes/standard/Images/24x24/msazure.png</icon>
      <domain>azure</domain>
    </identityProvider>

Note: make sure you update domain to point to newly configured azure domain. This ensures the new users we are creating are mapped to that domain (and thus able to access the mysitename site node)

Gotcha: Turns out the above is not sufficient to map users to new domain. Need to add the domain to main identity node as below:

<identityProviders>
  <identityProvider id="SitecoreIdentityServer" type="Sitecore.Owin.Authentication.IdentityServer.IdentityServerProvider, Sitecore.Owin.Authentication.IdentityServer" resolve="true">
    <domain>azure</domain>

This is a s a result of sitecore support request:

We discussed this behavior with the Product Team. The current design does not allow specifying different domain names for sub-providers.
The Domain property is similar to TriggerExternalSignOut and Transformations - it is inherited from the Identity Server provider node and cannot be overridden. Thus, the sub-provider will always have the domain that is specified for IdentityProvider. Based on this, users do not use the defaultProfileItemID attribute for the custom 'azure' domain.

2. Configure the <identityProvidersPerSites> node to specify which identity provider is used for which site (i.e. Shell, Admin, Extranet, DigitalPortal)

<identityProvidersPerSites>
 <mapEntry name="all sites">
  <identityProviders hint="list:AddIdentityProvider">
   <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='SitecoreIdentityServer/IdS4-AzureAd']" />
  </identityProviders>
 </mapEntry>
</identityProvidersPerSites>

3. Configure the <propertInitializer> node to map claims from Identity Server (Sitecore Profile Model) back to the New Profile (Defined in first step)

      <propertyInitializer>
        <maps>
...
          <map name="set UPN" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true">
            <data hint="raw:AddData">
              <source name="upn" />
              <target name="UPN" />
	        </data>
          </map>
...
        </maps>
      </propertyInitializer>

4. Finally, patch the site nodes to set the login page to new identity provider

    <sites>
      <site name="shell" set:loginPage="$(loginPath)shell/SitecoreIdentityServer/" />
      <site name="admin" set:loginPage="$(loginPath)admin/SitecoreIdentityServer/" />
      <site name="digitalportal" set:loginPage="$(loginPath)digitalportal/SitecoreIdentityServer/IdS4-AzureAd" />
    </sites>

Note: In the example above the Shell and Admin sites will redirect to the Identity Server Login page. Whereas the DigitalPortal site will bypass Identity Server and go straight to the Azure Login page. This is controlled by adding the sub-provider string at the end of the URL.

Create an Azure AD Application Group

  1. Navigate to Portal
  2. Create new Azure AD Application (take note of Application ID and also Tenant ID).
  3. Update Manfiest with the following:
    "groupMembershipClaims": "SecurityGroup",
  4. Also in Authentication tick the ID Tokens box

Setup Identity Server to use Azure AD and pass claims

First of all we need to update the Sitecore Profile Model to include the UPN that we want to pass back to Sitecore.

Go to: sitecore\Sitecore.Plugin.IdentityServer\Config\identityServer.xml and add UPN

      <IdentityResources>
        <SitecoreIdentityResource>
          <Name>sitecore.profile</Name>
          <UserClaims>
            <UserClaim1>name</UserClaim1>
            <UserClaim2>email</UserClaim2>
            <UserClaim3>role</UserClaim3>
            <UserClaim4>http://www.sitecore.net/identity/claims/isAdmin</UserClaim4>
            <UserClaim5>http://www.sitecore.net/identity/claims/originalIssuer</UserClaim5>
            <UserClaim6>comment</UserClaim6>
            <UserClaim7>upn</UserClaim7>
          </UserClaims>
          <Required>true</Required>
        </SitecoreIdentityResource>
      </IdentityResources>

The main configuration that needs updating to connect Sitecore Identity Server to Azure AD is found here: sitecore\Sitecore.Plugin.IdentityProvider.AzureAd\Config\Sitecore.Plugin.IdentityProvider.AzureAd.xml

Note the Authentication Scheme matches exactly the scheme specified in the above Sitecore configuration:

  1. Set Enabled to true and then add values for the Azure tenant and also the Active Directory Application Group
<Enabled>true</Enabled>
<ClientId>xx-xx-xx</ClientId>
<TenantId>xx-xx-xx</TenantId>

2. Map incoming claims From Azure To Sitecore Profile Model

<upnToUpn type="Sitecore.Plugin.IdentityProviders.DefaultClaimsTransformation, Sitecore.Plugin.IdentityProviders">
 <SourceClaims>
  <Claim1 type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" />
 </SourceClaims>
 <NewClaims>
  <Claim1 type="upn" />
 </NewClaims>
</upnToUpn>

3. Map groups (from groupMembershipClaims) in Azure Manifest, to roles in Sitecore.

<AzureADUserToReader type="Sitecore.Plugin.IdentityProviders.DefaultClaimsTransformation, Sitecore.Plugin.IdentityProviders">
 <SourceClaims>
  <Claim1 type="groups" value="xxx-group-id-from-azure-xxx" />
 </SourceClaims>
 <NewClaims>
  <Claim1 type="role" value="azure\reader"/>
 </NewClaims>
</AzureADUserToReader>

Note: this example maps a group of users from Azure to the reader role in azure domain. Because the role is within the azure domain, we can configure standard Sitecore access controls. Roles from other domains (i.e. extranet or Sitecore have no permissions within this new domain.

Other useful files in Sitecore Identity Server

One other file worth mentioning at this point is:

\Config\production\Sitecore.IdentityServer.Host.xml

In this file forms the second half of the link between Sitecore IS and the Sitecore Instance. Here you specify:

  • Certificate information
  • Connection string to Sitecore users database
  • Password recovery URL
  • URLs that can connect
  • Identity Secret

<?xml version="1.0" encoding="utf-8" ?>
<Settings>
  <Sitecore>
    <IdentityServer>
      <CertificateThumbprint>xxx</CertificateThumbprint>
      <CertificateStoreLocation>LocalMachine</CertificateStoreLocation>
      <CertificateStoreName>My</CertificateStoreName>
      <SitecoreMembershipOptions>
        <ConnectionString>xxx</ConnectionString>
      </SitecoreMembershipOptions>
      <AccountOptions>
        <PasswordRecoveryUrl>mysitename/sitecore/login?rc=1</PasswordRecoveryUrl>
      </AccountOptions>
      <Clients>
        <DefaultClient>
          <AllowedCorsOrigins>
            <AllowedCorsOriginsGroup1>mysitename</AllowedCorsOriginsGroup1>

          </AllowedCorsOrigins>
        </DefaultClient>
        <PasswordClient>
          <ClientSecrets>
            <ClientSecret1>xxx</ClientSecret1>
          </ClientSecrets>
        </PasswordClient>
      </Clients>
    </IdentityServer>
  </Sitecore>
</Settings>

Final Gotcha: Despite all the above, I was not seeing roles in user manager, yet they appeared to be in effect. The reason for this is because roles for virtual users are stored in cookies not in core database. This was discovery was made further to support request:

With regards to roles, please note that you will not see the roles of a user in the "MEMBER OF" tab of the "Edit User" dialog of the User Manager.
This is because these roles of external users are not stored in Sitecore. See the "Virtual and Persistent Users" section of the following article:
https://doc.sitecore.com/developers/93/sitecore-experience-manager/en/using-federated-authentication-with-sitecore.html

Leave a Reply

Your email address will not be published. Required fields are marked *