Sitecore 9.3 and integrating external authentication for a extranet site

J. Visser
2 min readMay 7, 2021

This is an interesting subject. We have an external identity provider for authentication for a sitecore website. We noticed that logging in took about 6–7 seconds.

We managed to find our solution deep inside the owin and sitecore implementation of sigin. Normally you would authorize the user with the following code.

await context.GetSignInManager().SignInAsync(userResolverResult.ApplicationUser, false, false).ConfigureAwait(false);

Because we don’t need the user data from sitecore for our extranet users we can skip the step step.

So we changed our login with the following piece of code.

context.GetSignInManager().AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = false }, claimsUser);

But there is an issue here, because the claims don’t contain the correct domain names within the user, we need to transform the claimuser to contain the domain.

public override void Process(InitializeArgs args)
{
var path = _settings.IdentityProcessingPathPrefix().EnsureTrailingSlash() + "externalauthenticationcallback";
args.App.Map(path, SessionStateBehavior.Required, app => app.Run(async context =>
{
var loginInfo = await context.Authentication.GetExternalLoginInfoAsync().ConfigureAwait(false);
if (loginInfo == null)
{
WebUtil.RedirectToErrorPage("Unsuccessful login with external provider.");
}
else
{
if (string.IsNullOrEmpty(loginInfo.DefaultUserName))
{
loginInfo.DefaultUserName = loginInfo.Email;
}
_logger.Info($"ExternalLoginInfo DefaultUsername='{loginInfo.DefaultUserName}' Claims='{string.Join("|", loginInfo?.ExternalIdentity?.Claims?.Select(s => s.Type + "=" + s.Value) ?? new string[0])}'");
using (TemporaryClaimsStorage.Init(loginInfo.ExternalIdentity, context.GetHttpContext()))
{
context.GetSignInManager().SignInAsync(userResolverResult.ApplicationUser, false, false);
var claimsUser = ClaimsIdentityFactory.TransformClaimsIdentity(loginInfo.ExternalIdentity);
context.GetSignInManager().AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = false, }, claimsUser);
}
var returnUrl = context.Request.Query["ReturnUrl"];
_logger.Info($"ExternalLoginInfo DefaultUsername='{loginInfo.DefaultUserName}', redirecting user to {returnUrl}");
WebUtil.Redirect(returnUrl);
}
}));
}

The transformation happens deep within owin, so we need to replicate the code.

public class OneGiniClaimsIdentityFactory 
{
private const string SecurityStampClaimType = "AspNet.Identity.SecurityStamp";
private const string UserNameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";
private const string UserIdClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
private const string IdentityClaimType = "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider";
private const string XmlStringType = "http://www.w3.org/2001/XMLSchema#string";
public static ClaimsIdentity TransformClaimsIdentity(ClaimsIdentity externalIdentity)
{
var userName = externalIdentity.Name;
// Change the default authenticationtype to Cookies
var identity = new ClaimsIdentity("Cookies");
// Reformat the UserId and UserNameClaim to include the dela domain
identity.AddClaim(new Claim(UserIdClaimType, $@"dela\{userName}", XmlStringType));
identity.AddClaim(new Claim(UserNameClaimType, $@"dela\{userName}", XmlStringType));
identity.AddClaim(new Claim(IdentityClaimType, "ASP.NET Identity", XmlStringType));
identity.AddClaim(new Claim(SecurityStampClaimType, string.Empty, XmlStringType));
// Exclude the UserIdClaimType and UserNameClaimType from the original claims
identity.AddClaims(externalIdentity.Claims.Where(s=>s.Type != UserIdClaimType && s.Type != UserNameClaimType));
return identity;
}
}

--

--

J. Visser

Sitecore, ContentStack, C# and Typescript/Javascript. working on unique performant solution