Override Displayed Username in Episerver backend

Howdy!

When implementing authentication via Open ID connect and OWIN, I ran into some issues with how Episerver (and I) handled usernames and user ids.

Some background as to why

Some features rely on IIdentity.Name to be the user name (presentable and pretty) while others rely on it being an immutable key. The latter is the case when using the Commerce PrincipalExtensions like GetCurrentContact() for example.

Behind the scenes GetCurrentContact() uses the MapUserKey method for resolving the serialized user id key persisted on the ContactEntity in BusinessFoundation. The input for the MapUserKey function is CurrentPrincipal.Identity.Name in this case, which then implies that the user key should indeed be an immutable unique key for the user.

In our case the user name may be changed, it is not static. Instead of integrating and replicating user name updates we set the user key to map to the id in the identity provider - in our case the sub claim from Open ID connect. The sub claim is also set to be the NameIdentifier in our OWIN startup flow.

So, with the above in place, we match keys and everything is dandy as long as we remember to use an extension method for getting the actual user name (like GetUserName() for ASP.NET Identity).

The issue

Bad user name...

Unfortunately, Episerver uses the IIdentity.Name in the top navigation bar in the edit/admin UI. This value is resolved in the ShellMenuProvider and I don't know any way of injecting another value short of hacking the menu provider system.

The workaround

Instead of hacking the existing provider I added another, which inserts a MenuItem with the sole purpose of injecting the actual user name (as well as the ellipsised) in the DOM as hidden input fields. Those fields are then used in a js module which overwrites the default values on load.

The menu provider:

[MenuProvider]
public class OverrideUserNameMenuProvider : IMenuProvider
{
  public IEnumerable<MenuItem> GetMenuItems()
  {
    return new[]
    {
      new OverrideUserNameMenuItem("doesNotDisplay", MenuPaths.User + "/override")
      {
        SortIndex = 9999
      }
    };
  }

  private class OverrideUserNameMenuItem : MenuItem
  {
    protected override void RenderContents(TextWriter writer)
    {
      var identity = HttpContext.Current?.User.Identity;
      if (identity == null || !identity.IsAuthenticated) return;

      var userName = identity.GetUserName();
      if (string.IsNullOrEmpty(userName) || string.Equals(userName, identity.Name, StringComparison.Ordinal)) return;

      writer.WriteLine($"<input type='hidden' id='replaceUsrName' value='{HttpUtility.HtmlAttributeEncode(userName)}' />");
      writer.WriteLine($"<input type='hidden' id='replaceUsrNameShort' value='{HttpUtility.HtmlAttributeEncode(userName.Ellipsis(20))}' />");
    }
  }
}

The javascript module snippet:

document.addEventListener(
  "DOMContentLoaded",
  function() {
    var userName = document.getElementById("replaceUsrName");
    if (userName != null) {
      document.querySelector(
        ".epi-navigation-global_user .epi-navigation-menuText"
      ).innerHTML = userName.value;
      var userNameShort = document.getElementById("replaceUsrNameShort");
      if (userNameShort != null) {
        document
          .querySelector(".epi-navigation-global_user")
          .setAttribute("title", userNameShort.value);
      }
    }
  },
  false
);

The module registration in module.config in web application root:

<?xml version="1.0" encoding="utf-8"?>
<module>
  <clientResources>
    <add name="navigation" path="~/ClientResources/Scripts/OverrideUserName.js" resourceType="Script"/>
  </clientResources>
</module>

It ain't pretty but at least the editors and admins will see their actual user names instead of the sub claim guid in the top bar.

Much better user name!

Cheers!