I'm using ASP.NET Core to build web server applications for my company. To authenticate users I'm using a library called Identity Server 4 with ASP.NET Core Identity. IS4 helps me to manage auth clients, resources easily. Which makes my life easier when I want to create a client for our web service.

But things never goes that happy. I wanted to use role based authentication to restrict some API endpoints to a group of users. But I found that IS4 is not adds required role claims to access tokens which is required for role based authorization.

So I wanted to write this blog post because it took my days to found a correct way to setting up role based authentication with IS4. Because, this is not documented on official documentation or even I did not found a proper answer to solve that issue. The issue is that Identity library requires role claims to be added to access tokens to authorize requests. But IS4 is not adding role claims by default You need to implement your own IProfileService or IClaimsPrincipalFactory service.

I choose to extend exists class named UserClaimsPrincipalFactory<TUser> from IdentityServer4.AspNetIdentity package.

namespace App.Services
{
    public class ClaimsFactory : UserClaimsPrincipalFactory<AppUser>
    {
        private readonly MyDbContext _context;
        private readonly UserManager<AppUser> _userManager;

        public ClaimsFactory(
            UserManager<AppUser> userManager,
            IOptions<IdentityOptions> optionsAccessor,
            MyDbContext context) : base(userManager, optionsAccessor)
        {
            _context = context;
            _userManager = userManager;
        }

        protected override async Task<ClaimsIdentity> GenerateClaimsAsync(AppUser user)
        {
            var identity = await base.GenerateClaimsAsync(user);
            var roles = await _userManager.GetRolesAsync(user);
            
            identity.AddClaims(roles.Select(role => new Claim(JwtClaimTypes.Role, role)));
            
            return identity;
        }
    }
}

We need to add ClaimsFactory to dependency injection using ConfigureServices.

public void ConfigureServices(IServiceCollection services)
{
    services.AddClaimsPrincipalFactory<ClaimsFactory>();
}

Now we can add Role claim type to our IS4 configuration. I created an IdentityResource with name "roles".

new IdentityResource
{
    Name = "roles",
    DisplayName = "Roles",
    UserClaims = { JwtClaimTypes.Role }
}

Add the resource (scope) name to Client's AllowedScopes list.

new Client
{
    ClientId = "native",
    ClientSecrets =
    {
        new Secret("secret".Sha256()),
    },
    AllowedScopes =
    {
        LocalApi.ScopeName,
        StandardScopes.OpenId,
        StandardScopes.Profile,
        StandardScopes.Email,
        StandardScopes.Phone,
        "roles",
    },
},

The last steep is to adding role claims to our ApiResource.

new ApiResource(LocalApi.ScopeName, "Local Api", new [] { JwtClaimTypes.Role }),
The logged in users needs to re-login to renew access token claims which will include role claims too.

That's all. Thanks for reading my blog ❤. You can enter your email bellow to get notified when I post something new.