March 21, 2016

Customising claims transformation in ASP.NET Core Identity

I’ve been testing out the new version of ASP.NET Identity and had the need to include additional claims in the ClaimIdentity generated when a user is authenticated.

Transforming Claims Identity

ASP.NET Core supports Claims Transformation out of the box. Just create a class that implements IClaimsTransformer:

public class ClaimsTransformer : IClaimsTransformer
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("ProjectReader", "true"));
        return Task.FromResult(principal);
    }
}

To register the claims transformer, add the following inside your Configure method in Startup.cs:

app.UseClaimsTransformation(new ClaimsTransformationOptions
{
    Transformer = new ClaimsTransformer()
});

One problem with the current implementation of the claims transformation middleware is that claims transformer instances have to be created during configuration. This means no DI making it difficult to handle loading claim information from a database.

Note: I’m told this will be fixed in RC2.

Claims Identity Creation

In my application I’d extended the default ApplicationUser class with additional properties for first and last name. I wanted these properties to be included in the generated ClaimsIdentity:

public class ApplicationUser : IdentityUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

ASP.NET Core Identity has a SignInManager<TUser> responsible for signing users into your application. Internally it uses a IUserClaimsPrincipalFactory<TUser> to generate a ClaimsPrincipal from your user.

The default implementation only includes username and user identifier claims. To add additional claims we can create our own implementation of IUserClaimsPrincipalFactory<TUser> or derive from the default UserClaimsPrincipalFactory:

public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public AppClaimsPrincipalFactory(
        UserManager<ApplicationUser> userManager,
        RoleManager<IdentityRole> roleManager,
        IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
    {
    }

    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);

        ((ClaimsIdentity)principal.Identity).AddClaims(new[] {
            new Claim(ClaimTypes.GivenName, user.FirstName),
            new Claim(ClaimTypes.Surname, user.LastName),
        });

        return principal;
    }
}

To register the custom factory we add the following to the ConfigureServices method in startup.cs after the services.AddIdentity() call:

services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();

© 2022 Ben Foster