I have an application using .NET Core 3.1 and also a frontend using the default React application, generated from this link.
In the .NET Core app, I have Identity Server setup with users and roles.
When I'm in the React app, I would like to know the roles from the user. I see that there's currently a library being used called oidc-client. 
From the responses I can debug when authorizing the user, I see that there are some scopes being returned.
scope: "openid profile [Name of the app]"
Here's the full response.

How can I know the roles from that user? 
Do I need to add it somewhere in my .NET Core app? 
Or can I figure it from the access_token in the response?
That template is using ASP.NET Core Identity to manage users/roles . So that the first thing is to enable roles :
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddRoles<IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();
Crating custom Profile service to include custom claims into tokens & userinfo endpoint :
public class ProfileService : IProfileService
{
    protected readonly UserManager<ApplicationUser> _userManager;
    public ProfileService(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }
    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        ApplicationUser user = await _userManager.GetUserAsync(context.Subject);
        IList<string> roles = await _userManager.GetRolesAsync(user);
        IList<Claim> roleClaims = new List<Claim>();
        foreach (string role in roles)
        {
            roleClaims.Add(new Claim(JwtClaimTypes.Role, role));
        }
        //add user claims
        roleClaims.Add(new Claim(JwtClaimTypes.Name, user.UserName));
        context.IssuedClaims.AddRange(roleClaims);
    }
    public Task IsActiveAsync(IsActiveContext context)
    {
        return Task.CompletedTask;
    }
}
And register in Startup.cs :
services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>()
        .AddProfileService<ProfileService>(); 
Now the claims will include in userinfo endpoint , your react application will automatically request the userinfo endpoint to get user's profile in getUser function of  AuthorizeService.js file , trace the _user.profile to get the new claims . Also , the role claims are included in access token .
You don't have to implement a ProfileService. The ReactJS+ID4 template already sets up a client (Client[0]) for the front end, you just have to add the appropriate configurations to have it place the role into the token.
        services
            .AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddRoles<IdentityRole>() //<- Very important, don't forget
            .AddEntityFrameworkStores<AuthDbContext>();
        services.AddIdentityServer()
            .AddApiAuthorization<ApplicationUser, AuthDbContext>(x =>
            {
                x.IdentityResources.Add(new IdentityResource("roles", "Roles", new[] { JwtClaimTypes.Role, ClaimTypes.Role }));
                foreach(var c in x.Clients)
                {
                    c.AllowedScopes.Add("roles");
                }
                foreach (var a in x.ApiResources)
                {
                    a.UserClaims.Add(JwtClaimTypes.Role);
                }
            });
On the client side, be careful using the role. It can either be a string or an array of strings based on how many roles the user is assigned to. I use the ensureArray function to help with this.
  isAdmin(user: User|null): boolean {
    return this.isInAnyRole(user, ["Admin"]);
  }
  isInAnyRole(user: User|null, requiredAnyRoles: string[]): boolean {
    var authorized = false;
    if (user) {
      var userRoles = this.ensureArray(user.profile.role);
      requiredAnyRoles.forEach(role => {
        if (userRoles.indexOf(role) > -1) {
          authorized = true;
        }
      });
    }
    return authorized;
  }
  private ensureArray(value: any): string[] {
    if (!Array.isArray(value)) {
      return [<string>value];
    }
    return value;
  }
Then you can add a policy on your server side.
services.AddAuthorization(options =>
{
     options.AddPolicy("RequireAdminRole", policy =>
     {
          policy.RequireClaim(ClaimTypes.Role, "Admin");
     });
});
Protect your api
[Authorize(Policy = "RequireAdminRole")]
[HttpPost()]
public async Task<IActionResult> Post([FromBody] CreateModel model)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With