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