Adding claims to existing identity

During moving one system from classic ASP.NET MVC to ASP.NET Core I faced an interesting challenge. Although access to system is based on Active Directory there is separate role management based on classic membership and roles providers. There are reasons why AD is not used for role management and I cannot change it. ASP.NET Core uses claims-based authentication and I needed to find a way to add role claims to authenticated identity. Here’s  the solution.

How things does not work

Adding claims to existing identity seems like small task to accomplish. But, well, it doesn’t go so easy. We can build middleware class and try something like shown here.

foreach(var role in user.Roles)
{
    var claim = new Claim(newIdentity.RoleClaimType, role.Name);
    identity.AddClaim(claim);
}

But it doesn’t work with existing identity. No errors, code runs smooth but role claims are just ignored.

Using claims transformation

There’s correct way to edit existing identity and it’s called claims transformation. Basically we have to write a custom class that implements IClaimsTransformation interface. Documentation doesn’t give much information about it but most important thing is said – we need to clone given identity.

In short, here’s how the process goes:

  1. Clone current user identity
  2. Add custom claims
  3. Return cloned identity

Here’s my claims transformation that adds roles to user identity. Notice that I can use dependency injection to inject instances of service classes to my claims transformation. Remember important trick – we need a clone of current identity to make things work.

public class AddRolesClaimsTransformation : IClaimsTransformation
{
    private readonly IUserService _userService;

    public AddRolesClaimsTransformation(IUserService userService)
    {
        _userService = userService;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        // Clone current identity
        var clone = principal.Clone();
        var newIdentity = (ClaimsIdentity)clone.Identity;

        // Support AD and local accounts
        var nameId = principal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier ||
                                                          c.Type == ClaimTypes.Name);
        if (nameId == null)
        {
            return principal;
        }

        // Get user from database
        var user = await _userService.GetByUserName(nameId.Value);
        if (user == null)
        {
            return principal;
        }

        // Add role claims to cloned identity
        foreach(var role in user.Roles)
        {
            var claim = new Claim(newIdentity.RoleClaimType, role.Name);
            newIdentity.AddClaim(claim);
        }

        return clone;
    }
}

The final thing to do is to register claims transformation with dependency injection in ConfigureServices() method of Startup class.

services.AddScoped<IClaimsTransformation, AddRolesClaimsTransformation>();

Considering you have authentication already configured and it works, the registered transform will get to authentication flow.

Wrapping up

Although adding few claims to existing identity seems like piece of cake, it is not so easy. We cannot just add claims to existing identity and hack it. Claims transformation as custom implementation of IClaimsTransformation interface is the tool we need to use to add claims to existing identity. As we saw we don’t modify existing identity but we clone it, add claims and then return the cloned instance.

Serverless360 Logo

A portal focused on Operations and Support for Microsoft Azure Serverless services

FREE TRIAL

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    6 thoughts on “Adding claims to existing identity

    • November 24, 2020 at 9:30 am
      Permalink

      I am so glad that you wrote this post.

    • November 26, 2020 at 6:41 pm
      Permalink

      Thank you, thank you, thank you. Been looking for this forever. Does this work with Blazor wasm as well?

    • November 26, 2020 at 8:27 pm
      Permalink

      I have made no experiments on Blazor yet. I needed this solution just for regular ASP.NET Core application.

    • February 10, 2021 at 10:37 am
      Permalink

      When does this run in the request pipeline?

      I’ve done this using middleware with great success but this indeed this seems like the standard approach.

    • March 11, 2021 at 11:13 am
      Permalink

      does not work, it also returns a 500 but no exception whatsoever, which makes it hard to figure what’s going on

      also, what if one need a db injection, to check the user role ?

    • April 2, 2021 at 3:21 pm
      Permalink

      I find that TransformAsync gets called every time a page is loaded, and the principal does not have any of the claims that I added on the previous page load, which means that I have to add them again. This means I have to call the database every time to get the user information that I use to create the claims.

      Is this normal? Is there a way to persist the claims between pages? Using .Net Core 3.1 and Windows auth.

    Leave a Reply

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