Menu
the REFERENCE

Federated Authentication in Sitecore 9 with Custom Claims Using Identity Server 3

Federated Authentication in Sitecore allows you to authenticate users into the Sitecore CMS through an external auth provider. This post aims to provide guidance on how to achieve this, as well as demonstrating some powerful configuration options at your disposal when handling the mapping of claims. I will demonstrate how to take claims from Identity Server 3 and even add custom claims that can be processed by the Owin configuration.

Getting Started

While the very basic approach of configuring federated authentication can be achieved with just a few modifications to configuration files (see here for more details), this post will override Identity Provider processing and thus requires some code as well.

To adhere to Helix guidelines, I created a new project beneath Foundation called Foundation. Authentication

Once this is done, you’ll need to include the following Nuget Packages for the project:

  • Sitecore.owin (Sitecore repo)
  • Sitecore.Owin.Authentication.NoReferences (Sitecore repo)
  • Sitecore.Owin.Client.NoReferences (Sitecore repo)
  • Microsoft.owin
  • Microsoft.Owin.Security.OpenIdConnect
  • Microsoft.AspNetCore.Identity

Sitecore.Owin.Authentication.Enabler

The Sitecore Owin Authentication Enabler is responsible for handling the external providers and miscellaneous configuration necessary to authenticate. For anything you are doing with Federated Authentication, you need to enable and configure this file. By default this file is disabled (specifically it comes with Sitecore as a .example file). You can see a vanilla version of this file in your Sitecore directory at:

\App_Config\Include\Examples\Sitecore.Owin.Authentication.Enabler.config.example

While I don’t think it matters too much as there won’t be conflicting overrides, as a personal preference I placed a copy of this file in my App_Config\Environments folder, however you may choose to do something else such as store it in your zzz.Foundation folder. Be sure to remove the .example extension so it is live.

https://gist.github.com/karbyninc/01b91d39375c189b1a92d9bcfc162352

Let’s look at a few key sections:

Federated Authentication in Sitecore 9 with Custom Claims

This section is where you would define your list of identity providers. There is a provision to include multiple (and apply different processing of claims). To add your identity provider, add a 'identityprovider' tag as I did above, and give it an id. This is a custom identifier so you can pick whatever you want to call it (mine is called idsrv because I’m using identity server, but I could have just as easily called it ids3 or something else).

Within each identity provider, you can specify what the login button will be when you visit the Sitecore login page. This is pretty cool as you have control over the name and even the icon that appears on the new login button.

This is controlled within each 'identityprovider' section with the following XML:

Federated Authentication in Sitecore 9 with Custom Claims

Federated Authentication in Sitecore 9 with Custom Claims

Applying Per-Provider Transformations, and Transformations for All Providers

For each provider, there is a section to allow for claims transformations. Each one resides in the 'transformation' tag and you can put any name you want as the value.

This can be a bit frustrating to work with, because essentially what has to happen is the claims must match on key and value, so you have to get it right. Studying sample output from your authentication service is helpful. So in my scenario below, based on the user logging in, there was be a claim for ‘xrole’ with a value of ‘developer’, or ‘author’. I then set the Sitecore role accordingly. So in essence what the code below does is set the Sitecore role for the user logging in. I’ve also seen examples of people using information that comes back from Azure, such as Group Id, etc., to determine if a user belongs to a particular group or anything else you want o match on. You can list as many source/targets as you want, and the underlying middleware will aim to match the source name and value. If a match is found, it will then change the claim’s name and value to what you want to transform it to (in the target section), effectively replacing the claim. If you want to add a new claim, and keep your original one, you can do so by adding the tag 'keepsource'true'/keepsource' (by default this is false).

Federated Authentication in Sitecore 9 with Custom Claims

 

In addition to each provider, you can also access add a 'sharedtransformations' node and put the same sources/targets pairings in there. The difference is that this will apply the specified transformations to all providers.

 

Property Initializers

Property initializers allow you to take claims and map them to Sitecore fields stored on a user profile. You can also access the claim in your code by the new name. This is great if, for example, you want to standardize the way you access a particular claim (say your code always uses the field “email” but different providers may pass you a diff claim name).

This works in conjunction with the transformations above – you can normalize all of the claims being sent in from disparate sources, map them to one single field, and then map them to the sitecore user profile below. For example if we had one provider give us “user_email” and another give us “UserEmail” as claims, we could transform them both to “email” and then map it to the “email” property in the user profile.

While my configuration below lacks the value attribute, you can add it to make a more specific match, for example:

Federated Authentication in Sitecore 9 with Custom Claims

would replace the claim x with a value of 1, with a claim name=y, value=2.

Federated Authentication in Sitecore 9 with Custom Claims

Building External Users

As mentioned above, I wrote custom code to extend how a user is created when they authenticate. That part is referenced here in the 'externaluserbuilder' node.

Federated Authentication in Sitecore 9 with Custom Claims

Here, you can specify custom code to handle when a user is created. I referenced my class “CreateUniqueUser” located in the Foundation.Authentication assembly. We’ll look at this code shortly.

Next, you’ll notice the flag “isPersistentUser” above, which allows you to determine if the user will be saved after the session is closed. To quote Sitecore regarding this property:

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

Let’s look at the code now to see how we can override the default user creation during authentication:

https://gist.github.com/karbyninc/a8528ce40c6015bae95460acd716a70b

While in most cases you can get by just fine using your transformations and property initializers, it’s powerful to have the capability to extend this by using your own custom code to override how a user is created in Sitecore. You have control over what domains are set, what the final username is, or accessing/setting really any other property on the user profile.

Adding Code for the Provider

The most important part of this process is now writing the actual provider code. We made reference to our custom code here in the configuration section:

Federated Authentication in Sitecore 9 with Custom Claims

It is now time to implement that code responsible for authentication.

https://gist.github.com/karbyninc/f8121bf101c079b53e8e18be89132933

In most cases, common implementations of Federated Auth in Sitcore simply use the values from their claims token, map them to fields, and call it a day (with the heavy lifting happening in the configuration file itself). I think this is how it was intended, and is perfect in most cases, however for me I needed additional information not being set on the initial claims during authentication. As a result, I needed to retrieve additional information and process it within C#.

Here, I will show you how I retrieved a first and last name, and then concatenated them, added it to a custom claim, and then mapped that to a Sitecore field during user creation.

// Get userinfo data by using our access token to retrieve data from the authority's /connect/userinfo endpoint.
var userInfoClient = new Thinktecture.IdentityModel.Client.UserInfoClient(new System.Uri(n.Options.Authority + "/connect/userinfo"), n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
var sidentity = n.AuthenticationTicket.Identity;
userInfo.Claims.ToList().ForEach(ui => sidentity.AddClaim(new Claim(ui.Item1, ui.Item2)));

So this code here connects to the userinfo endpoint and retrieves the additional information I needed. It sorts through each claim that was given and adds it to my sidentity variable.

Finally, I want to do something with the claims – specifically look at the first and last name, and map them to a full name.

var firstName = "";
var lastName = "";

//Retrieve the claim given_name, and assign to first_name
if (userInfo.Claims.ToList().FirstOrDefault(k => k.Item1 == "given_name") != null)
firstName = userInfo.Claims.ToList().FirstOrDefault(k => k.Item1 == "given_name").Item2;

//The claim "family_name" is what was getting returned from the info
if (userInfo.Claims.ToList().FirstOrDefault(k => k.Item1 == "family_name") != null)
lastName = userInfo.Claims.ToList().FirstOrDefault(k => k.Item1 == "family_name").Item2;

//Add a custom claim, which is then transformed to the Sitecore FullName field.
sidentity.AddClaim(new Claim("UserFullName", firstName + " " + lastName));

//Apply transformations using our rules in the Sitecore.Owin.Authentication.Enabler.config
foreach (var claimTransformationService in identityProvider.Transformations)
claimTransformationService.Transform(sidentity, new TransformationContext(_configuration, identityProvider));

So this retrieves the given_name and family_name claims, concatenates them together, and then adds them as a new claim called UserFullName.

You might stop and ask “Why didn’t your server just return the mapped user properties as one full name and then you wouldn’t have to do any of this processing yourself”! I could have done that instead, obviating the need to write any mappings and code, however this is a simple example to demonstrate just how much power you have over this. You can utilize your middleware implementation to achieve a tremendous amount of customization in claims management and the underlying integration with Sitecore.

If you remember from the configuration, I had specified the following in the property initializers:

Federated Authentication in Sitecore 9 with Custom Claims

So this “UserFullName” isn’t something that came from Identity Server on its own – this was the property we created ourselves! Despite that, it is still processed all the same in the code:

foreach (var claimTransformationService in identityProvider.Transformations)
claimTransformationService.Transform(sidentity, new TransformationContext(_configuration, identityProvider));

and mapped directly to the “FullName” user profile field in Sitecore

In addition, we created another custom claim xComment, that I wanted to map to the Sitecore user profile “Comment” property. This was done in our property initializers in the configuration file:

Federated Authentication in Sitecore 9 with Custom Claims

Now when your user logs in, they will have the custom claims we set!

Conclusion

While the basis of federated authentication in Sitecore is really quite simple, requiring some tweaks to a configuration file and overriding ProcessCore(IdentityProvidersArgs args) in a class that implements IdentityProvidersProcessor, you can see how we took things even further by hooking into the code responsible for creating a new user in Sitecore to customize the domain and username.

In addition, we saw how to retrieve additional information from our endpoint, process the claims, and even create our own custom claim that was picked up by the property initializers.

Interesting in learning more?

Reach Out Today

Don't miss out

top
The Reference has its office in the heart of Manhattan.
“I want to wake up in that city that never sleeps, and find I'm king of the hill, top of the list, head of the heap” – Frank Sinatra