Login to Sitefinity with external ID (instead of email) using OpenIdConnect

As of Sitefinity 10, you can configure external identity providers. This allows you to login to Sitefinity with credentials from an external system. This is great, unless your external provider doesn’t use email as a static identifier for users. Sitefinity doesn’t allow the email field to be updated on demand, and it looks at the email claim from the external provider to determine what user account in Sitefinity to log in. If it does not find a user matching the email, it will create a brand new user.

Here is an example

  1. You are Jane Doe (jane.doe@starkindustries.com)
  2. You log into Sitefinity for the first time using the external login setup by your company
    1. Sitefinity creates a new user account for you. Every time you login again, it will match your external provider email with the jane.doe@starkindustries.com email it has setup on the new user account.
  3. You get married, and now you are Jane Roe. Your company changes your name and email address in their system (jane.roe@starkindustries.com).
  4. You log back into Sitefinity using the external login
    1. Sitefinity doesn’t know jane.roe@starkindustries.com, so it creates a new user account for you. All of your previous profile data is now orphaned.

How to solve it

Note: This tutorial assumes you have already setup a OpenIdConnect external identity provider

We need to tell Sitefinity to use some other kind of static identifier for login in the external system. Unfortunately, because¬†it is hard-wired to use the email for login/lookup, we will need give Sitefinity a static identifier in the form of an email address. For this, we will need to be creative. We will tell Sitefinity to take the identifier from the external system and append that to a fake email “@accounts.starkindustries.com” (e.g. 1234@accounts.starkindustries.com).

First, we will customize our SecurityTokenValidated method (OpenIdConnectAuthenticationNotifications).

        private Task SecurityTokenValidatedInternal(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
            var identity = notification.AuthenticationTicket.Identity;

            var externalUserId = identity.FindFirst("externalId"); // This is the static identifier from external system
            if(externalUserId != null)
                identity.AddClaim(new Claim(SitefinityClaimTypes.ExternalUserEmail, externalUserId.Value + "@accounts.starkindustries.com"));

            var externalUserEmail = identity.FindFirst("email_address") != null ? identity.FindFirst("email_address").Value : string.Empty;
            var externalUserFirstName = identity.FindFirst("given_name") != null ? identity.FindFirst("given_name").Value : string.Empty;
            var externalUserFamilyName = identity.FindFirst("family_name") != null ? identity.FindFirst("family_name").Value : string.Empty;
            identity.AddClaim(new Claim(SitefinityClaimTypes.ExternalUserName, string.Format("{0}|{1} {2}", externalUserEmail, externalUserFirstName, externalUserFamilyName)));

            return Task.FromResult(0);

Pay close attention to what we are doing with the email and the user name. For the email, we are creating an email which will always be static (using the external ID). For the username, we are doing something hacky, but necessary. We are taking the real email address of the user, first name, last name, and combining/piping them so we can break them down later.

What’s this hack for?

In Sitefinity’s authentication code, it takes the ExternalUserName claim, splits it by a single space character, and then uses the two parts for the First Name and Last Name in the Sitefinity profile. Also, Sitefinity typically uses the ExternalUserEmail claim for populating the email address for the user. But, we still want to know the users real email, so we are adding that to the user name claim and will add additional parsing logic to pull it out.

As of right now, our users will have these fields in their profile after their first login:

  • First name: jane.doe@starkindustries.com|Jane
  • Last name: Doe
  • Email: 1234@accounts.starkindustries.com

We are going to take the email out of the first name, and store that in the users email address. And we will take their static identifier email and store that in their Username field in Sitefinity. We desire this:

  • First name: Jane
  • Last name: Doe
  • Email: jane.doe@starkindustries.com
  • Username: 1234@accounts.starkindustries.com

I know what you are thinking. The static identifier email is no longer in the email field! It’s okay. Sitefinity has a fallback in its lookup code. It first looks in the Email field, and then checks the Username field. As of Sitefinity 10, you can find this in the Telerik.Sitefinity.Security.UserUtils class.

Alright, in order to modify the user’s profile data after Sitefinity creates the user for the first time, we are going to use the EventHub. Specifically, the ProfileCreating event. You’ll need to register the event at some point during the initialization of the application.

EventHub.Subscribe<ProfileCreating>(evt => ProfileEventHandler(evt));
        public void ProfileEventHandler(ProfileCreating eventInfo)
            var profile = eventInfo.Profile;
            var providerName = "OurExternalProviderName"; // This is the name of the external provider from AuthenticationConfig.config
            if (profile.GetType() == typeof(SitefinityProfile) && profile.User.ExternalProviderName == providerName)
                var sfProfile = profile as SitefinityProfile;
                if (sfProfile.FirstName.IndexOf("|") > 0)
                    var staticIdentifier = profile.User.Email;
                    var nameParts = sfProfile.FirstName.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);

                    sfProfile.User.Email = nameParts[0];
                    if (nameParts.Length > 1)
                        sfProfile.FirstName = nameParts[1];
                        sfProfile.FirstName = string.Empty;

And there we have it. This profile event will fix our user account in Sitefinity and set the static identifier to live in the UserName field. Now if we were to login as jane.roe@starkindustries.com, Sitefinity would use our existing account instead of creating a new one – because it is able to find a match based on the username, 1234@accounts.starkindustries.com. Obviously, the external provider is only sending us the “1234” part, we are appending the accounts.starkindustries.com in our OIDC provider (it’s the first code snippet in the post).

Happy coding!

Leave a Reply

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