Sitecore 9 Federated Authentication with IdentityServer3, Endless Loop

Viewed 2206

I've been struggling to get Federated Authentication working with Sitecore 9 using IdentityServer 3 as the IDP. I've followed the example seen in http://blog.baslijten.com/enable-federated-authentication-and-configure-auth0-as-an-identity-provider-in-sitecore-9-0/ for Auth0, and have converted that to IDS3. But what I experienced was an endless loop between the IDP and Sitecore.

It seems that upon authentication, IdentityServer 3 redirects back to Sitecore, which fails to convert the authentication into a cookie. I'm left with a .nonce cookie instead. Sitecore, not seeing an authenticated user, redirects over to the IDP and this continues until I stop the process.

My IdentityProviderProcessor (with dummy values):

using System.Threading.Tasks;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using Sitecore.Diagnostics;
using Sitecore.Owin.Authentication.Configuration;
using Sitecore.Owin.Authentication.Pipelines.IdentityProviders;
using Sitecore.Owin.Authentication.Services;

namespace xx.xxxx.SC.Foundation.Authentication
{
    public class IdentityProviderProcessor : IdentityProvidersProcessor
    {
        public IdentityProviderProcessor(FederatedAuthenticationConfiguration federatedAuthenticationConfiguration) : base(federatedAuthenticationConfiguration)
        {

        }

        /// <summary>
        /// Identityprovidr name. Has to match the configuration
        /// </summary>
        protected override string IdentityProviderName
        {
            get { return "ids3"; }
        }

        protected override void ProcessCore(IdentityProvidersArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            IdentityProvider identityProvider = this.GetIdentityProvider();
            string authenticationType = this.GetAuthenticationType();

            args.App.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "Cookies"
            });

            args.App.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                Authority = "xxxx",
                ClientId = "xxxx",
                Scope = "openid profile xxxx",
                RedirectUri = "xxxx",
                ResponseType = "id_token token",
                SignInAsAuthenticationType = "Cookies",
                UseTokenLifetime = false,
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    SecurityTokenValidated = (context) =>
                    {
                        var identity = context.AuthenticationTicket.Identity;

                        foreach (Transformation current in identityProvider.Transformations)
                        {
                            current.Transform(identity, new TransformationContext(FederatedAuthenticationConfiguration, identityProvider));
                        }

                        var virtualUser = Sitecore.Security.Authentication.AuthenticationManager.BuildVirtualUser("xxxx\\user@domain.com", true);

                        // You can add roles to the Virtual user
                       virtualUser.Roles.Add(Sitecore.Security.Accounts.Role.FromName("extranet\\MyRole"));

                        // You can even work with the profile if you wish
                        virtualUser.Profile.SetCustomProperty("CustomProperty", "12345");
                        virtualUser.Profile.Email = "user@domain.com";
                        virtualUser.Profile.Name = "My User";

                        // Login the virtual user
                        Sitecore.Security.Authentication.AuthenticationManager.LoginVirtualUser(virtualUser);

                        return Task.FromResult(0);
                    },
                },
            });
        }
    }
}

And my config file:

<?xml version="1.0" encoding="utf-8"?>
<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>
        <!-- Processors for coniguring providers. Each provider must have its own processor-->
        <processor type="xx.xxxx.SC.Foundation.Authentication.IdentityProviderProcessor, xx.xxxx.SC.Foundation.Authentication" resolve="true" />
      </owin.identityProviders>
    </pipelines>

    <federatedAuthentication type="Sitecore.Owin.Authentication.Configuration.FederatedAuthenticationConfiguration, Sitecore.Owin.Authentication">
      <!--Provider mappings to sites-->
      <identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
        <!--The list of providers assigned to all sites-->
        <mapEntry name="all sites" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication">
          <sites hint="list">
            <sites hint="list">
              <site>modules_website</site>
              <site>website</site>
            </sites>
          </sites>
          <identityProviders hint="list:AddIdentityProvider">
            <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='ids3']" />
          </identityProviders>
          <externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication">

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

          </externalUserBuilder>
        </mapEntry>

      </identityProvidersPerSites>

      <!--Definitions of providers-->
      <identityProviders hint="list:AddIdentityProvider">
        <!--Auth0 provider-->
        <identityProvider id="ids3" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication">
          <param desc="name">$(id)</param>
          <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
          <!--This text will be showed for button-->
          <caption></caption>
          <icon></icon>
          <!--Domain name which will be added when create a user-->
          <domain>sitecore</domain>
          <!--list of identity transfromations which are applied to the provider when a user signin-->
          <transformations hint="list:AddTransformation">
            <!--SetIdpClaim transformation-->
            <transformation name="set idp claim" ref="federatedAuthentication/sharedTransformations/setIdpClaim" />
          </transformations>
        </identityProvider>
      </identityProviders>
      <sharedTransformations hint="list:AddTransformation">
      </sharedTransformations>
    </federatedAuthentication>
  </sitecore>
</configuration>

Note that the only way I could accomplish this was to create a VirtualUser on validation. Given the near-total lack of documentation on this topic, I'm not sure if this is a needed step, or if something is wrong with the way I've set this up.

For now, the VirtualUser works like a champ and we'll likely keep this in place. I would like to know, however, is the creation of a VirtualUser here required or did I do something wrong?

Thanks for any input.

1 Answers
Related