Sitecore Experience Management

Configure federated authentication

Abstract

Describes how to configure federated authentication

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

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. 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.

The App_config\Include\Examples\Sitecore.Owin.Authentication.Enabler.config.example file does two things:

  • It patches the sitecore/services configuration node by configuring a dependency injection to replace implementations of the Sitecore.Abstractions.BaseAuthenticationManager, Sitecore.Abstractions.BaseTicketManager and Sitecore.Abstractions.BasePreviewManager classes with implementations that work with OWIN authentication.

  • It patches the FederatedAuthentication.Enabled setting by setting it to true.

If you enable this config file by removing the example extension, Sitecore applies these two patches.

Note

Be aware of these potential problems if you enable this config file:

DI patches are applied, but FederatedAuthentication.Enabled is false. In this case, ASP.NET Identity is used, but an API for retrieving the external login links always returns nothing and external authentication endpoints will not work.

DI patches are not applied, but FederatedAuthentication.Enabled is set to true. In this case, the SitecoreConfigurationException error will be thrown at startup.

Note

For Sitecore 9.0, update 1, on Azure, you must open the web.config and change "false" to "true" in this setting:

<add key="owin:AutomaticAppStartup" value="true" />

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:

<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>

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.

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:

    <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 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.

Note

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

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:

<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.

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 shows an example of doing this:

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

    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:

    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:

    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:

    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:

    <services>

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

    </services>

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:

    using Sitecore.Owin.Authentication.Extensions;

    [...]

    IOwinContext context = HttpContext.Current.GetOwinContext();

    UserManager<ApplicationUser> userManager = context.GetUserManager();

  2. Use these methods for CRUD operations:

    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);

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 inhertis 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:

    <externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication">

    <param desc="isPersistentUser">true</param>

    </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:

<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">
         <param desc="isPersistentUser">true</param>
    </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).