Sitecore Federated Authentication – Part 3 – Sitecore User and Claims Identity

If you have followed my previous post, I hope you should now be able to login to Sitecore using External Identity Provider. In this post, we will see more about Claims Identity and store required values in Sitecore User Profile also we’ll create a user with the user’s email address instead of the hash code.

Claims Identity

Claims-based identity is a common way for applications to acquire the identity information they need about users inside their organization, in other organizations, and on the Internet.

Both Google and Facebook provide different claim identity name and value. So in order to bind properly, we have to update the configuration as below. You should explore Facebook Graph API from Facebook and OAuth 2.0 Playground from Google in order to get more information about the user.

In order to store the Full Name value of a user in Sitecore, I was trying to add http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name claim directly to a Full Name Property in Sitecore User Profile for a user in Property Initializer Mapping. But each time I try to add it always store sitecore\APTixbqulVz0qp5xEbNrkA in the Full Name instead storing Nikki Punjabi as a name, which I was getting from both the identity providers as a claim value.

Solution:

<!--Add Full Name Claim Transformation-->
<transformation name="name" type="Sitecore.Owin.Authentication.Services.DefaultTransformation,Sitecore.Owin.Authentication">
	<sources hint="raw:AddSource">
	        <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" />
	</sources>
	<targets hint="raw:AddTarget">
	        <claim name="FullName" />
	</targets>
</transformation>

Added a new claim by copying the value of name from the external identity provider into FullName claim and then in Property Initializer copy the value of FullName claim to Sitecore User Property FullName.

Update Property Initializer Mapping:

<!--Property mappings initializer-->
    <propertyInitializer type="Sitecore.Owin.Authentication.Services.PropertyInitializer, Sitecore.Owin.Authentication">
		<!--List of property mappings
			Note that all mappings from the list will be applied to each providers-->
		<maps hint="list">
		
			<!--The mapping sets the Email property of the user profile from emailaddress claim-->
              <map name="email claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
                <data hint="raw:AddData">
				  <!--claim name-->
				  <source name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" />
				  <!--property name-->
				  <target name="Email" />
				</data>
              </map>
			  
			  <map name="comment claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
                <data hint="raw:AddData">
				  <!--claim name-->
				  <source name="idp" />
				  <!--property name-->
				  <target name="Comment" />
				</data>
              </map>
			  
			  <map name="name claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
                <data hint="raw:AddData">
				  <!--claim name-->
				  <source name="FullName" />
				  <!--property name-->
				  <target name="FullName" />
				</data>
              </map>
		
			<map name="FacebookAdmin" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
				<data hint="raw:AddData">
				  <!--claim name-->
				  <source name="idp" value="Facebook" />
				  <!--property name-->
				  <target name="IsAdministrator" value="true" />
				</data>
			</map>
			
			<map name="GoogleAdmin" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
				<data hint="raw:AddData">
				  <!--claim name-->
				  <source name="idp" value="Google" />
				  <!--property name-->
				  <target name="IsAdministrator" value="true" />
				</data>
			</map>
		</maps>
	</propertyInitializer>

So now, we have added email, name and comment claim. Email Claim will add the email address in the Email Address Property of Sitecore User, similarly, Name Claim will go in Full Name and Comment Claim in the Comment Property. Note: Here we haven’t provided the value in the target property so it will set whatever value we get in the Claim Name from the respective Identity Provider.

In Facebook Identity Provider, I was not getting the email and name properly, in order to get that we have to set the Field and Scope in the FacebookAuthentication.cs.

Now if you try to login with external Identity Provider, you’ll get all the required information in User Profile.

That’s great!

Create Sitecore User with Email Address

It looks fine with the above information getting stored in Sitecore User Profile. One can easily understand that from where the user is authenticated and logged in to the system. Still, if you would like to go with Username that you can override CreateUniqueUserName method.

public class CreateUniqueUser : DefaultExternalUserBuilder
    {
        public CreateUniqueUser(string isPersistentUser) : base(bool.Parse(isPersistentUser)) { }

        [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login")]
        protected override string CreateUniqueUserName(UserManager<ApplicationUser> userManager, ExternalLoginInfo externalLoginInfo)
        {
            //TODO: If Email
            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");
            string domain = identityProvider.Domain;
            return domain + "\\" + externalLoginInfo.Email;

        }
    }

Once you have added the above class we need to update the config file.

Update <externalUserBuilder> as below for Facebook and Google in mapEntry:

<externalUserBuilder type="OWINAuthentication.IdentityProviders.CreateUniqueUser, OWINAuthentication">
	<param desc="isPersistentUser">true</param>
</externalUserBuilder>

Now, when you test facebook or google login, you’ll see the actual user is getting created with sitecore\user-email-id instead of hash code.

Unable to create a user. Reason: DuplicateUserName

In this case, I faced an issue with the same email address. I am having the same email address for Facebook and Google, therefore after signing in once with Facebook and then with Google, I get the above error. So the quickest solution I found was to create a separate domain for Facebook and Google and update domain with facebook and google respectively in the <identityProvider> configuration.

<!--Domain name which will be added when creating a user-->
<domain>facebook</domain>
<!--Domain name which will be added when creating a user-->
<domain>google</domain>

There could be another solution to this. This series was done as a part of exploring the topic. If you have any other approach, please feel free to share in the comments below.

Reference:
Mapping property in Sitecore 9 federated authentication

Happy Authenticating! 🙂

One thought to “Sitecore Federated Authentication – Part 3 – Sitecore User and Claims Identity”

  1. Another solution to the “DuplicateUserName” error in case you want to use same user (domain) regardless of federated auth provider, is to override Sitecore.Owin.Authentication.Services.DefaultApplicationUserResolver

    Override the method ResolveApplicationUserAsync and add code to find and attach an existing user if it exists rather than always trying to CreateUser (which is usually called immediately after it calls your customised ExternalUserBuilder). You have to decompile and review the default code to ensure your implementation works in all scenarios.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.