Configure federated authentication

Version: 9.3

You use federated authentication to let users log in to Sitecore through an external provider. Federated authentication requires that you configure Sitecore in a specific way, depending on which external provider you use. Configuring federated authentication involves a number of tasks:

Configure an identity provider

You must configure the identity provider you use. How you do this depends on the provider you use. The primary use case is to use Azure Active Directory (Azure AD). Authorize access to web applications using OpenID Connect and Azure Active Directory describes how Azure AD works.

To configure an identity provider:

  1. Patch the configuration/sitecore/federatedAuthentication/identityProviders node by creating a new node with the name identityProvider.

  2. Enter values for the id and type attributes. The type must implement the abstract class Sitecore.Owin.Authentication.Configuration.IdentityProvider. Sitecore has a default implementation –Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider.

  3. Under the node you created, enter values for the param, caption, domain, and transformations child nodes.

  4. Under the configuration/sitecore/federatedAuthentication/identityProvidersPerSites node, create a new node with name mapEntry.

  5. Enter values for the name and type attributes. The value of the name attribute must be unique for each entry. The type must be Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication, or inherit from this.

  6. Enter true as the value of the resolve attribute.

  7. Under the node you created, enter values for the sites (the list of sites where the provider(s) will work), identityProviders (the list of providers), and externalUserBuilder child nodes. Enter true as the value of the resolve attribute of each externalUserBuilder node.

    Note

    This also applies for SXA sites.

Add code for the provider

You must create a new processor for the owin.identityProviders pipeline.

To create a new processor:

  1. Inherit the Sitecore.Owin.Authentication.Pipelines.IdentityProviders.IdentityProvidersProcessor class.

  2. Override the IdentityProviderName property with the name you specified for the identityProvider in the configuration.

  3. Override the ProcessCore method.

Integrate with the owin.identityProviders pipeline

Next, you must integrate the code into the owin.identityProviders pipeline.

For example, this sample uses Azure AD as the identity provider:

RequestResponse
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentDelivery or ContentManagement">
    <pipelines>
      <owin.identityProviders>
        <processor type="Sitecore.Owin.Authentication.YourIdentityProviders.AzureAd, YourAssembly" resolve="true" />
      </owin.identityProviders>
    </pipelines>
  </sitecore>
</configuration>  

Sitecore user name generation

User names must be unique across a Sitecore instance. You cannot use user names from different external providers as Sitecore user names because this does not guarantee that the user names are unique.

The DefaultExternalUserBuilder class creates a sequence of user names for a given external user name. It then uses the first of these names that does not already exist in Sitecore. The values in the sequence depend only on the external username and the Sitecore domain configured for the given identity provider.

Map claims and roles

A provider issues claims and gives each claim one or more values. Sitecore reads the claims issued for an authenticated user during the external authentication process. You can restrict access to some resources to identities (clients or users) that have only specific claims.

An external user is a user that has claims. Mapping claims to roles allows the Sitecore role-based authentication system to authenticate an external user.

To map claims to roles:

  1. Add an <identityProvider> node to configuration/sitecore/federatedAuthentication/identityProviders.

  2. Add a <transformations hint="list:AddTransformation"> node to the <identityProvider> node.

  3. Add transformation nodes as child nodes.

    For example, a transformation node looks like this:

    RequestResponse
    <transformation type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
      <sources hint="raw:AddSource">
        <claim name="groups" value="f04b11c5-323f-41e7-ab2b-d70cefb4e8d0" />
        <claim name="groups" value="40901f21-29d0-47ae-abf5-184c5b318471" />
      </sources>
      <targets hint="raw:AddTarget">
        <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="Sitecore\Developer" />
      </targets>
      <keepSource>true</keepSource>
    </transformation>
    • The type must inherit from the Sitecore.Owin.Authentication.Services.Transformation class.

    • In this example, the transformation adds a claim with the name http://schemas.microsoft.com/ws/2008/06/identity/claims/role and the value Sitecore\Developer to those identities that have two claims with the name group and values f04b11c5-323f-41e7-ab2b-d70cefb4e8d0 and 40901f21-29d0-47ae-abf5-184c5b318471 at the same time.

    • keepSource==true specifies that the original claims (two group claims, in this example) will not be removed. The default is false, and this means that if the transformation is successfully applied to the identity, then the original claims are replaced with the ones that are stated in the <targets> nodes.

    • keepSource==true specifies that the original claims (two group claims, in this example) will not be removed. In Sitecore, for Sitecore.Owin.Authentication.Services.DefaultTransformation, keepSource has a default value of false, meaning that if the transformation is successfully applied to the identity, the original claims are replaced with the ones that are stated in the <targets> nodes.

      Note

      On the Identity Server, for Sitecore.Plugin.IdentityProviders.DefaultClaimsTransformation, keepSource has a default value of true.

Note

If you specify claims transformations in the sitecore/federatedAuthentication/sharedTransformations node, these transformations are for all identity providers.

Map properties

You must map identity claims to the Sitecore user properties that are stored in user profiles.

The propertyInitializer node, under the sitecore\federatedAuthentication node, stores a list of maps. Each map has inner source and target nodes. These nodes have two attributes: name and value. You map properties by setting the value of these properties.

If a claim matches the name attribute of a source node (and value, if specified), the value attribute of a user property specified by the name attribute of a target node is set to the value of the matched claim (if the value attribute is not specified in the target node).

For example:

RequestResponse
<propertyInitializer type="Sitecore.Owin.Authentication.Services.PropertyInitializer, Sitecore.Owin.Authentication">
  <maps hint="list">
    <map name="status to UserStatus"
      type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
      <data hint="raw:AddData">
        <source name="status" value="first" />
        <target name="UserStatus" value="1" />
      </data>
    </map>
  </maps>
</propertyInitializer>

In this example, the source name and value attributes are mapped to the UserStatus target name and value 1.

Important

If you split up your configuration files, you must add the name attribute to the map nodes to make sure that your nodes are unique across all the files. This is due to the way Sitecore config patching works.

Map custom users properties

You can map custom user properties in two places where the Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper can store them:

  • If a user is virtual, regardless of whether the property is custom or not, it is stored in the Sitecore.Security.Accounts.User class as .RuntimeSettings.Properties property.

  • If a user is persistent, it is stored in the Sitecore.Security.Accounts.User class as .Profile.CustomProperties property.

For debugging or development purposes, it is possible to look at these properties but they are private properties and are not expected to be accessed directly.  

For custom functionality, where you need to access these properties, you can access them through the Sitecore.Security.Accounts.User class Profile property's indexer.  This resolves the user property from the correct location regardless of whether the user is virtual or persistent.

Connect a user account

An account connection allows you to share profile data between multiple external accounts on one side and a persistent account on the other side. If a persisted user has roles assigned to them, federated authentication shares these with the external accounts.

Under the following circumstances, the connection to an account is automatic. Sitecore signs out the authenticated user, creates a new persistent or virtual account, and then authenticates it:

  • The user is already authenticated on the site.

  • The user signs in to the same site with an external provider.

  • There is not already a connection between an external identity and an existing, persistent account. In ASP.NET Identity, signInManager.ExternalSignIn(...) then returns SignInStatus.Failure.

To bind the external identity to an already authenticated account, you must override the Sitecore.Owin.Authentication.Services.UserAttachResolver class using dependency injection. The following steps show an example of doing this:

  1. Extend the Sitecore.Owin.Authentication.Services.UserAttachResolver class:

    RequestResponse
    using System;
    
    using System.Threading.Tasks;
    
    using Microsoft.Owin;
    
    using Sitecore.Owin.Authentication.Services;
    
    using Sitecore.Text;
    
    namespace Sitecore.Owin.Authentication.Samples.Services
    
    {
    
        public class SampleUserAttachResolver : UserAttachResolver
    
        {
    
            public override UserAttachResolverResult Resolve(UserAttachContext context)
    
            {
    
                IFormCollection formData = Task.Run(async () => await context.OwinContext.Request.ReadFormAsync()).Result;
    
                string consentResult = formData["uar_action"];
    
                UserAttachResolverResultStatus resultStatus;
    
                if (Enum.TryParse(consentResult, true, out resultStatus))
    
                {
    
                    return new UserAttachResolverResult(resultStatus);
    
                }
    
                string redirectUrl = new UrlBuilder("/dialogs/consent") { ["returnUrl"] = context.ReturnUrl }.ToString();
    
                context.OwinContext.Response.Redirect(redirectUrl);
    
                return new UserAttachResolverResult(UserAttachResolverResultStatus.DelayedResolve);
    
            }
    
        }
    
    }

    The Resolve method takes UserAttachContext as a value argument, sends a request to the controller, and handles the answer from the controller that it calls.

  2. Create an endpoint by creating an MVC controller and a layout.

    The MVC controller:

    RequestResponse
    using System.Web.Mvc;
    
    namespace Sitecore.Owin.Authentication.Samples.Controllers
    
    {
    
        public class ConsentController : Controller
    
        {
    
            public ActionResult Index()
    
            {
    
                this.ViewBag.User = this.HttpContext.User.Identity.Name;
    
                this.ViewBag.ReturnUrl = this.Request.Params["ReturnUrl"];
    
                return this.View();
    
            }
    
        }
    
    }

    The layout:

    RequestResponse
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Consent window</title>
      </head>
    
      <body>
        <p>
          The @ViewBag.User user is already logged in. Would you like to attach to
          the user or create new record?
        </p>
    
        <br />
    
        <form method="POST" action="@ViewBag.ReturnUrl">
          <button type="submit" name="uar_action" value="attach">Attach</button>
    
          <button type="submit" name="uar_action" value="new">New</button>
        </form>
      </body>
    </html>
  3. Register the extended class in Sitecore by creating a new service configurator class:

    RequestResponse
    using Microsoft.Extensions.DependencyInjection;
    
    using Sitecore.DependencyInjection;
    
    using Sitecore.Owin.Authentication.Samples.Services;
    
    using Sitecore.Owin.Authentication.Services;
    
    namespace Sitecore.Owin.Authentication.Samples.Infrastructure
    
    {
    
        public class ServicesConfigurator : IServicesConfigurator
    
        {
    
            public void Configure(IServiceCollection serviceCollection)
    
            {
    
                serviceCollection.AddSingleton<UserAttachResolver, SampleUserAttachResolver>();
    
            }
    
        }
    
    }
  4. Define the created class in a custom configuration file, by adding following node under the <sitecore> node:

    RequestResponse
    <services>
    
      <configurator
        type="Sitecore.Owin.Authentication.Samples.Infrastructure.ServicesConfigurator, Sitecore.Owin.Authentication.Samples" />
    
    </services>

Programmatic account connection management

Sitecore uses the ASP.NET Identity for account connections, so account connections are handled in an identical way to the ASP.NET Identity API:

  1. Retrieve a UserManager object from the Owin context:

    RequestResponse
    using Sitecore.Owin.Authentication.Extensions;
    
    // [...]
    
    IOwinContext context = HttpContext.Current.GetOwinContext();
    
    UserManager<ApplicationUser> userManager = context.GetUserManager();
  2. Use these methods for CRUD operations:

    RequestResponse
    Task<IdentityResult> AddLoginAsync(ApplicationUser user, UserLoginInfo login);
    
    Task<IdentityResult> RemoveLoginAsync(ApplicationUser user, UserLoginInfo login);
    
    Task<IList<UserLoginInfo>> GetLoginsAsync(ApplicationUser user);
    
    Task<ApplicationUser> FindAsync(UserLoginInfo login);

Configure virtual and persistent users

Sitecore supports virtual users. When you authenticate users through external providers, Sitecore creates and authenticates a virtual user with proper access rights.

However, there are some drawbacks to using virtual users. User profile data cannot be persisted across sessions, as the virtual user profile exists only as long as the user session lasts. You should therefore create a real, persistent user for each external user. When a user uses external authentication for the first time, Sitecore creates and persists a new user, and binds this user to the external identity provider and the user ID from that provider. The next time that the user authenticates with the same external provider and the same credentials, Sitecore finds the already created and persisted user and authenticates it.

User builder

The identityProvidersPerSites/mapEntry node contains an externalUserBuilder node. Add a user builder like this:

  • Specify a class that inherits from Sitecore.Owin.Authentication.Services.ExternalUserBuilder. The user builder is responsible for creating a Sitecore user, based on the external user info.

    The default implementation that you configure to create either persistent or virtual users is based on the isPersistentUser constructor parameter:

    RequestResponse
    <externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication">
        <IsPersistentUser>true</IsPersistentUser>
    </externalUserBuilder>
Note

When you implement the user builder, you must not use it to create a user in the database. It must only create an instance of the ApplicationUser class.

Applying builder to site

Find mapEntry within the identityProvidersPerSites node of the site that you are going to define a user builder for, and specify the externalUserBuilder node. For example:

RequestResponse
<mapEntry type="Sitecore.FederatedAuthentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.FederatedAuthentication">
    <sites hint="list">
        <site>shell</site>
        <site>admin</site>
        <site>website</site>
    </sites>
    <identityProviders hint="list:AddIdentityProvider">mapEntry
        <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='AzureAd']" />
        <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='IdentityServer']" />
    </identityProviders>
    <externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication">
         <IsPersistentUser>true</IsPersistentUser>
    </externalUserBuilder>
</mapEntry>

In the example above, Sitecore applies the builder to the shell, admin, and websites sites.

The applied builders override the builders for the relevant site(s).

Do you have some feedback for us?

If you have suggestions for improving this article,