Enrich Your Claims When Customizing ASP.NET Identity User

VP, Enterprise Platforms
Valtech

September 24, 2018

ASP.NET Identity is a powerful replacement for the older (and weaker) membership system Optimizely uses out-of-the-box.

One of the largest weaknesses with the old identity management framework, like Membership, was that it was too cumbersome to customize the storage. With the introduction of ASP.NET Identity a few years back, they’ve achieved a clear separation of the storage of the identity information (e.g. username, password, etc.) from the code that implements the security (e.g., password hashing, password validation, etc.).
This is achieved by placing all the account related data behind an interface, IUser, and the storage operations behind another interface, IUserStore. If we are to customize the definition of a user, like adding a new personal characteristic, we simply create a custom implementation of IUser and use it as part of your Startup (not covered with this blog-post).

public class CustomerIdentityUser : IdentityUser, IUIUser 
{
  /// <summary>
  /// Access rights
  /// </summary>
  public CustomerIdentityUserAccessRight? AccessRight { get; set; }

}

[Flags]
public enum CustomerIdentityUserAccessRight
{
    ReadOnly = 1, //Can only read
    Modify = 2, //Can modify
    Manage = 4 //Can manage 

    /*
     *  0000001 = 1
     *  0000010 = 2
     *  0000100 = 4
     *  0001000 = 8
     *  0010000 = 16
     *  0100000 = 32
     *  1000000 = 64
     */ 
}

Above introduces a new characteristic called AccessRight, which describes the do’s and don’ts for a given user. Relying on this information throughout our application, like in Optimizely’s VirtualRoles, requires us to enrich the ClaimsIdentity with this new piece of information.

public class CustomizedClaimsIdentityFactory : ClaimsIdentityFactory<CustomerIdentityUser>
{   
    public override async Task<ClaimsIdentity> CreateAsync(UserManager<CustomerIdentityUser, string> manager, CustomerIdentityUser user,
        string authenticationType)
    {
        if (user == null)
            return null; //If there's no user, we can't generate an identity

        ClaimsIdentity identity = await base.CreateAsync(manager, user, authenticationType);

        if ((user.AccessRight & CustomerIdentityUserAccessRight.ReadOnly) == CustomerIdentityUserAccessRight.ReadOnly)
            identity.AddClaim(new Claim(ClaimTypes.AccessRightClaimType, "readonly"));

        if ((user.AccessRight & CustomerIdentityUserAccessRight.Modify) == CustomerIdentityUserAccessRight.Modify)
            identity.AddClaim(new Claim(ClaimTypes.AccessRightClaimType, "modify"));

        if ((user.AccessRight & CustomerIdentityUserAccessRight.Manage) == CustomerIdentityUserAccessRight.Manage)
            identity.AddClaim(new Claim(ClaimTypes.AccessRightClaimType, "manage"));

        return await Task.FromResult(identity);
    }
}

It somewhat speaks for itself! In order to register the new enrichment mechanism, you need to overwrite the ClaimsIdentityFactory property during the initialization of your UserManager.

manager.ClaimsIdentityFactory = new CustomizedClaimsIdentityFactory();

When you’re looking to leverage the new claims you’ve added, simply access the ClaimsIdentity and use the HasClaims method – here’s an example relying on Optimizely’s VirtualRoles.

class IsAuthenticatedAsManagerAccess : VirtualRoleProviderBase
{
    public override bool IsInVirtualRole(IPrincipal principal, object context)
    {
        if (principal.Identity == null)
            return false;

        if (principal.Identity is ClaimsIdentity)
        {
            ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity;

            //If user has a manager access right claim
            if (claimsIdentity.HasClaim(c => c.Type.Equals(ClaimTypes.AccessRightClaimType) && c.Value.Equals("manage")))
                return true;
        }

        return false;
    }
}

Happy Coding!
 

Contact us

We would love to hear from you! Please fill out the form and the nearest person from office will contact you.

Let's reinvent the future