With the release of Sitecore 9.1, Sitecore no longer supports the Active Directory module from the Marketplace. This tool helps with integrating an on-premise Sitecore instance with the organization’s Active Directory (AD) setup so that admins and authors can sign in to the platform with their network credentials.  

Instead, this new version of Sitecore introduces Identity Server (IS) – a separate identity provider that makes it easier to set up single sign-on (SSO) across all Sitecore services and applications. 

We recently helped a client upgrade a Sitecore website from version 7.2 to version 9.1.1 and make the transition to using IS. As there is not much documentation on how best to achieve this switch, we decided to document and share the approach we followed.

Two Different Identity Provider Approaches for a Sitecore SSO Setup

The task was to figure out how to connect Identity Server to the client’s Active Directory. In reading through the official Sitecore documentation, we determined that there are two main approaches you can take. In the first approach, you can connect Sitecore directly to an identity provider via Federated Authentication.

                                          | → Sitecore Identity Server (available out of the box)

Sitecore Instance →       | → Azure AD

                                          | → ADFS

                                          | → Some other provider

Basically, you are configuring Sitecore to work with some other identity provider. Out of the box, Sitecore is configured to use Identity Server. This, in turn, is configured to use the traditional ASP.NET Membership Provider for regular sign in, using SQL Server and the Core database – a method we have been familiar with for many years. So, in this approach, we would not really be using Identity Server at all for an Active Directory integration.

The second approach uses Identity Server as a Federation Gateway to external systems.

                                                                                                  | → Facebook

Sitecore Instance → Sitecore Identity Server →                  | → Azure AD

                                                                                                  | → ADFS

                                                                                                  | → Some other provider

In this approach, you are isolating the different identity providers from Sitecore by using a middleman. Sitecore isn’t aware of the different providers and just communicates with Identity Server, which can be configured and modified to support the involved provider. We decided to take this second approach as it seemed more modular and simpler to update over time.

Making Sure Identity Server Is Working Properly

Before attempting any integration tasks, I tried just opening a browser and going to the Identity Server URL. I got the following 500 Error: “The requested page cannot be accessed because the related configuration data for the page is invalid.” It pointed to the Identity Server web.config file.

After evaluating this, I realized that the Identity Server website is built on top of .NET Core and by default IIS does not support hosting a .NET Core website. So, I found a way around this and installed the .NET Core 2.2 Runtime and Hosting Bundle for Windows. Once I installed this, my Identity Server loaded without issue! 

Note* - This step may only be necessary if you are running Windows 10. I believe that Windows Servers have this hosting bundle installed by default.

First Attempt – Connecting to ADFS

In talking with the client, they mentioned that they had Active Directory Federation Services (ADFS) available. So, we went down that path. I found an example of someone that had done this, which seemed pretty straight forward and also utilized the Federation Gateway approach that we wanted to use. 

However, we ran into multiple issues when trying to follow this solution. Ultimately, we determined that the client’s ADFS server was a much older version (2012 r2) than what we had read about in other blog posts.

The normal supported version was ADFS 2016. This likely meant that their ADFS server would not be able to connect with IS because it didn’t support the OpenID Connect protocols. It was at this point that we changed gears to Azure AD. 

Second Attempt – Connecting to Azure AD

Using Azure AD is supported out of the box with Sitecore 9.1.x and you can learn more about how to do this in this great writeup. The basic steps are as follows:

  1. In Azure AD, create a new Application Registration by going to the App Registrations tab and clicking on New Registration.
  2. Azure will ask you for a Name and a Redirect URI.  Give it any name you want and for the Redirect URI enter the base URL for your Identity Server followed by “signin-oidc”. It should look like this: “https://<Identity Server Host Name>/signin-oidc”
  3. Once you have done that, you should be able to get the Application ID (Client ID) and the Directory ID (Tenant ID) of the newly created App registration from the Overview tab.
  4. Next, click on the Authentication tab and make sure that the ID Tokens checkbox is checked in the Advanced Settings section.
  5. Go to the Manifest tab and change the “groupMembershipClaims” value from NULL to “SecurityGroup”. This will tell Azure AD to send back information about the Security Groups that the current user belongs to.
  6. Now edit the Azure AD config file on the Identity Server. It is located at [Identity Server Root]\sitecore\Sitecore.Plugin.IdentityProvider.AzureAd\Config\Sitecore.Plugin.IdentityProvider.AzureAd.xml.  Inside that file there are 4 main things to modify:
    1. Display Name - this is the text that will display on the button on the sign-in page
    2. Enabled - set this to “true”
    3. ClientId - set this to the Application ID from step 3 above
    4. TenantId - set this to the Directory ID from step 3 above
  7. Save everything and recycle the App Pools for both the Identity Server and your Sitecore instance.  If everything is working properly, when you go to http://mySitecore.local/sitecore it should redirect you to https://myIdentity.local and you should see the Identity Server sign-in screen.
  8. You should be able to click the “Azure AD” button, authenticate against your Azure AD instance, and then get redirected back to Sitecore. However, when you get back to Sitecore you should receive a message telling you that you don’t have access to the system. Just because you authenticated against Azure AD doesn’t mean you have access to Sitecore!

Mapping Azure AD Security Groups to Sitecore Roles

To provide access to Sitecore you need to map Azure AD Security Groups to Security Roles in Sitecore. You can do this by editing the same XML file that you did before - [Identity Server Root]\sitecore\Sitecore.Plugin.IdentityProvider.AzureAd\Config\Sitecore.Plugin.IdentityProvider.AzureAd.xml.  

First, you need to know the GUID for the Azure AD Security Group that you want to map. In Azure AD, find the Security Group and get its Object ID. Then, inside the ClaimsTransformations section, add the following node and paste in the Object ID of the Azure AD group.

<AzureADUserToSitecoreUser1 type="Sitecore.Plugin.IdentityProviders.DefaultClaimsTransformation, Sitecore.Plugin.IdentityProviders">
  <SourceClaims>
    <Claim1 type="groups" value="fb62bf98-12ad-43b9-97a5-23c689ee9324" />
  </SourceClaims>
  <NewClaims>
    <Claim1 type="role" value="sitecore\Sitecore Client Authors" />
  </NewClaims>
</AzureADUserToSitecoreUser1>

What this is telling Identity Server is that you want to map the Security Group with that Object ID to the Sitecore role of “sitecore\Sitecore Client Authors” (or whatever role you want to put that person in). You can create as many of these mappings as you need.

Now after saving and recycling app pools, you should be able to complete the sign-in through Azure AD and successfully log in to Sitecore!

Customizing the Sitecore Domain & Usernames

One thing you will notice after you sign in to Sitecore is that your username in the upper right-hand corner is a random series of letters.  If you would like your username and email to be set properly just follow these instructions. They will help you understand how to map claims by editing the config file in the Identity Server site and also editing a config file in Sitecore.  At the end of this process, you should have your Sitecore username and email set properly.

One thing we noticed in our implementation, however, was that by default the users that signed in through Azure AD were automatically placed in the Sitecore domain and their actual Sitecore username was still a random series of 10 letters.

Normally, this wouldn’t be a problem. But since this was an upgrade, we wanted to preserve the old domain and usernames that authors had from the previous system to ensure that existing Sitecore security role membership would still apply. So, we needed to figure out how to get these new users in the custom domain from the previous site and override the name that was created.

To customize the domain, we simply edited the following file on the Sitecore CM instance: [Sitecore Root]\App_Config\Sitecore\Owin.Authentication.IdentityServer\Sitecore.Owin.Authentication.IdentityServer.config

We edited the following node:  configuration | sitecore | federatedAuthentication | identityProviders | identityProvider and set <domain> equal to the value of our domain in Sitecore.  The code looks like this:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore role:require="Standalone or ContentDelivery or ContentManagement">
    <sc.variable name="identityServerAuthority" value="my Identity Server URL goes here" />

    <settings>  ... </settings>

    <services> ... </services>

    <pipelines> … </pipelines>

    <federatedAuthentication>
      <identityProvidersPerSites> … </identityProvidersPerSites>
      <identityProviders>
        <identityProvider id="SitecoreIdentityServer" type="Sitecore.Owin.Authentication.IdentityServer.IdentityServerProvider, Sitecore.Owin.Authentication.IdentityServer" resolve="true">
          <caption>Go to login</caption>
          <domain>Your Domain Name Goes Here</domain>
          <enabled>true</enabled>

This tells Sitecore that any user created using the Identity Server Provider goes in our custom domain.

The last piece of the puzzle was to figure out a way to override the username assigned by Sitecore. In our situation, we needed to use part of the user’s email address as their username.  Their email address in the Azure AD system had the format of [CompanyID]@company.com and we wanted their Sitecore username to take the form of [Domain]\[CompanyID].

Out of the box Sitecore has a DefaultExternalUserBuilder class that has a method called “CreateUniqueUserName”.  All we had to do was override that method with our own class and then patch it in the correct place in the config.  To do this, we first created a class of our own that looks like this:

using System;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Sitecore.Diagnostics;
using Sitecore.Owin.Authentication.Configuration;
using Sitecore.Owin.Authentication.Identity;
using Sitecore.Owin.Authentication.Services;
using Sitecore.SecurityModel.Cryptography;

namespace MySolution.Authentication
{
    public class CustomExternalUserBuilder : DefaultExternalUserBuilder
    
        public CustomExternalUserBuilder() { }
        public CustomExternalUserBuilder(bool isPersistentUser) : base(isPersistentUser) { }

        public CustomExternalUserBuilder(string isPersistentUser) : base(isPersistentUser) { }

        public CustomExternalUserBuilder(ApplicationUserFactory applicationUserFactory, IHashEncryption hashEncryption) : base(applicationUserFactory, hashEncryption) { }
        protected override string CreateUniqueUserName(UserManager<ApplicationUser> userManager, ExternalLoginInfo externalLoginInfo)
        
            Assert.ArgumentNotNull((object)userManager, nameof(userManager));
            Assert.ArgumentNotNull((object)externalLoginInfo, nameof(externalLoginInfo));
            IdentityProvider identityProvider = this.FederatedAuthenticationConfiguration.GetIdentityProvider(externalLoginInfo.ExternalIdentity);
            if (identityProvider == null)
                throw new InvalidOperationException("Unable to retrieve identity provider for given identity");

            var companyId = "";

            foreach (var claim in externalLoginInfo.ExternalIdentity.Claims)
            
                if (claim.Type.ToLower() == "email" && !string.IsNullOrEmpty(claim.Value))
                
                    if (claim.Value.IndexOf("@") > 0)
                    
                        var atSignIndex = claim.Value.IndexOf("@");
                        companyId = claim.Value.Substring(0, atSignIndex);
                        break;

            string domain = identityProvider.Domain;
            return domain + "\\" + companyId; 
}

Then, we edited the following file in our Sitecore instance:  [Sitecore Root]\App_Config\Sitecore\Owin.Authentication\Sitecore.Owin.Authentication.config.

We searched for “externalUserBuilder” in that file and replaced it with this:

<externalUserBuilder type="MySolution.Authentication.CustomExternalUserBuilder, MySolution">
  <IsPersistentUser>true</IsPersistentUser>
</externalUserBuilder>

This tells Sitecore to use our custom class instead of the default class. Now, when a new user signs in via Azure AD, their Sitecore user account will be placed in the correct domain and will have the desired username. Of course, if you have different requirements for how a username should be constructed you can use your own logic instead.

Configuring Your Sitecore 9.1 Instance to Work with Azure AD

If you’re upgrading to Sitecore 9.1.x and need to integrate Sitecore Identity Server with Azure Active Directory for your SSO needs, we hope that this post can guide you through the process.

Please do join the conversation by commenting below. We’d love to know if you’re running into any challenges and how you’ve managed to resolve them. If you have further questions and would like to pick our brain on the topic, you can also reach out via email or Twitter.