1

I've run into a dead end with Federated authentication in Sitecore 9.1. I've implemented a IdentityProvidersProcessor using Microsoft.Owin.Security.OpenIdConnect to be able to authenticate using users from our Auth0 setup as extranet users. Everything works nicely, the users are persisted and claims are mapped to properties on the user, except for roles. Here's my implementation of the IdentityProvidersProcessor:

args.App.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        {
            Caption = identityProvider.Caption,
            Scope = "openid profile email",
            AuthenticationType = "Auth0",
            AuthenticationMode = AuthenticationMode.Active,
            ResponseType = OpenIdConnectResponseType.CodeIdToken,
            SignInAsAuthenticationType="Cookies",                
            ClientId = auth0ClientId,
            Authority = authority,
            ClientSecret = auth0ClientSecret,

            RedirectUri = auth0RedirectUri,
            PostLogoutRedirectUri = auth0PostLogoutRedirectUri,




            TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidIssuer = authority
            },

            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                SecurityTokenValidated = notification =>
                {
                    var identity = notification.AuthenticationTicket.Identity;
                    foreach (var claimTransformationService in identityProvider.Transformations)
                    {
                        claimTransformationService.Transform(identity,
                            new TransformationContext(FederatedAuthenticationConfiguration, identityProvider));

                    }
                    notification.AuthenticationTicket = new AuthenticationTicket(identity, notification.AuthenticationTicket.Properties);
                    return Task.CompletedTask;
                },
                RedirectToIdentityProvider = notification =>
                {
                    if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                    {
                        var logoutUri = $"https://{auth0Domain}/v2/logout?client_id={auth0ClientId}";

                        var postLogoutUri = notification.ProtocolMessage.PostLogoutRedirectUri;
                        if (!string.IsNullOrEmpty(postLogoutUri))
                        {
                            if (postLogoutUri.StartsWith("/"))
                            {
                                // transform to absolute
                                var request = notification.Request;
                                postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
                            }
                            logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
                        }

                        notification.Response.Redirect(logoutUri);
                        notification.HandleResponse();
                    }                        
                    return Task.FromResult(0);
                }
            }
        });

Here's my implementation of the DefaultExternalUserBuilder:

public class Auth0ExternalUserBuilder : DefaultExternalUserBuilder
{
    public Auth0ExternalUserBuilder(): this(true)
    {
    }

    public Auth0ExternalUserBuilder(bool isPersistentUser): 
        this(
            ServiceLocator.ServiceProvider.GetService<ApplicationUserFactory>(), 
            ServiceLocator.ServiceProvider.GetService<IHashEncryption>())
    {
        IsPersistentUser = IsPersistentUser;
    }

    public Auth0ExternalUserBuilder(string isPersistentUser) :
        this(
            ServiceLocator.ServiceProvider.GetService<ApplicationUserFactory>(),
            ServiceLocator.ServiceProvider.GetService<IHashEncryption>())
    {
        IsPersistentUser = bool.Parse(isPersistentUser);
    }

    public Auth0ExternalUserBuilder(ApplicationUserFactory applicationUserFactory, IHashEncryption hashEncryption) : base(applicationUserFactory, hashEncryption)
    {

    }
    public override ApplicationUser BuildUser(
        UserManager<ApplicationUser> userManager,
        ExternalLoginInfo externalLoginInfo)
    {
        ApplicationUser user = this.ApplicationUserFactory.CreateUser(this.CreateUniqueUserName(userManager, externalLoginInfo));
        user.IsVirtual = !this.IsPersistentUser;
        return user;
    }
    protected override string CreateUniqueUserName(Microsoft.AspNet.Identity.UserManager<ApplicationUser> userManager, ExternalLoginInfo externalLoginInfo)
    {
        IdentityProvider identityProvider = this.FederatedAuthenticationConfiguration.GetIdentityProvider(externalLoginInfo.ExternalIdentity);
        if (identityProvider == null)
            throw new InvalidOperationException("Unable to retrieve identity provider for given identity");
        var username = "";
        foreach (var c in externalLoginInfo.ExternalIdentity.Claims)
        {
            if (c.Type.Equals("user_name"))
                username = c.Value;
        }

        string domain = identityProvider.Domain;
        return domain + "\\" + username.Replace('|', '-');
    }
}

And here's my configuration:

<pipelines>
  <owin.identityProviders>
    <processor type="Helix.Foundation.Authentication.IdentityProviders.Auth0IdentityProviderProcessor, Helix.Foundation.Authentication" resolve="true" />
  </owin.identityProviders>
</pipelines>
<federatedAuthentication>
  <identityProviders>
    <identityProvider id="Auth0" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication">
      <param desc="name">$(id)</param>
      <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
      <domain>MyDomain</domain>
      <transformations hint="list:AddTransformation">
        <transformation name="set idp claim" ref="federatedAuthentication/sharedTransformations/transformation[@name='set idp claim']" />
        <transformation name="usernameClaim" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
          <sources hint="raw:AddSource">
            <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" />
          </sources>
          <targets hint="raw:AddTarget">
            <claim name="user_name" />
          </targets>
          <keepSource>true</keepSource>
        </transformation>
        <transformation name="use exp claim for authentication cookie expiration" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
          <sources hint="raw:AddSource">
            <claim name="exp" />
          </sources>
          <targets hint="raw:AddTarget">
            <claim name="http://www.sitecore.net/identity/claims/cookieExp" />
          </targets>
          <keepSource>true</keepSource>
        </transformation>

        <transformation name="Roles" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
          <sources hint="raw:AddSource">
            <claim name="idp" value="Auth0" />
          </sources>
          <targets hint="raw:AddTarget">
            <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="MyDomain\Test" />
          </targets>
          <keepSource>true</keepSource>
        </transformation>

      </transformations>
    </identityProvider>
  </identityProviders>
  <propertyInitializer type="Sitecore.Owin.Authentication.Services.PropertyInitializer, Sitecore.Owin.Authentication">
    <maps hint="list">
      <map name="portrait claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
        <data hint="raw:AddData">
          <source name="picture" />
          <target name="Portrait" />
        </data>
      </map>
      <map name="email claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
        <data hint="raw:AddData">
          <source name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" />
          <target name="Email" />
        </data>
      </map>
      <map name="FullName claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
        <data hint="raw:AddData">
          <source name="name" />
          <target name="FullName" />
        </data>
      </map>
      <map name="comment claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
        <data hint="raw:AddData">
          <source name="idp" />
          <target name="Comment" />
        </data>
      </map>

      <map name="Username claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
        <data hint="raw:AddData">
          <source name="name" />
          <target name="user_name" />
        </data>
      </map>

    </maps>
  </propertyInitializer>
  <identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
    <mapEntry name="Auth0" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication" patch:before="mapEntry[@name='all sites']">
      <sites hint="list">
        <site>website</site>
        <site>mysite</site>
        <site>mysite2</site>
      </sites>
      <identityProviders hint="list:AddIdentityProvider">
        <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='Auth0']" />
      </identityProviders>
      <externalUserBuilder type="Helix.Foundation.Authentication.ExternalUserBuilders.Auth0ExternalUserBuilder, Helix.Foundation.Authentication">

        <IsPersistentUser>true</IsPersistentUser>

      </externalUserBuilder>
    </mapEntry>
  </identityProvidersPerSites>
</federatedAuthentication>

To my understanding of the Sitecore documentation revolving federated authentication found here this part should do the trick:

<transformation name="Roles" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
      <sources hint="raw:AddSource">
        <claim name="idp" value="Auth0" />
      </sources>
      <targets hint="raw:AddTarget">
        <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="MyDomain\Test" />
      </targets>
      <keepSource>true</keepSource>
    </transformation>

And when debugging the SecurityTokenValidated notification where the transformation is being applied I can actually see that the new claim is created for the role. Yet no role is being added to the persisted user. Am I missing something here? Should I write the code my self in my implementation of the DefaultExternalUserBuilder to add these roles? I can't really find anything about this in the Sitecore documentation or the community

  • i'm currently trying to implement the same thing as well, but i'm facing some issues. The context user is never logged in to sitecore, the context user is always "extranet/Anonymous". i was thinking that may be it's something related to the DefaultExternalUserBuilder but i don't get where the DefaultExternalUserBuilder is actually called, can you please help me? – AmiraElEwady Dec 15 '19 at 18:09
  • Did you get the solution for this problem? – Nehemiah Jeyakumar Aug 11 at 17:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.