I have a SPA that has an ASP.NET Core web API together with the inbuilt identity server switched on using AddIdentityServer
and then AddIdentityServerJwt
:
services.AddIdentityServer()
.AddApiAuthorization<User, UserDataContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
I also have an authorization policy setup that requires an "Admin" role claim:
services.AddAuthorization(options =>
{
options.AddPolicy("IsAdmin", policy => policy.RequireClaim(ClaimTypes.Role, "Admin"));
});
I have a controller action that uses this policy
[Authorize(Policy = "IsAdmin")]
[HttpDelete("{id}")]
public IActionResult Deleten(int id)
{
...
}
The authenticated user does have the "Admin" role claim:
The access token for this authentication user doesn't appear to contain the admin claim:
I get a 403 back when trying to request this resource with the admin user:
So, if I'm understanding this correctly, IdentityServer isn't including the admin role claim and so the user isn't authorized to access the resource.
Is it possible to configure the claims that IdentityServer uses using AddIdentityServerJwt
? or am I misunderstanding why this is not working.
IdentityServer emits claims about users and clients into tokens. You are in full control of which claims you want to emit, in which situations you want to emit those claims, and where to retrieve those claims from.
IdentityServer is a free and open-source framework that has been used by thousands of companies to develop their single sign-on solutions. For the last four years, Rock Solid Knowledge has been working with the creators of IdentityServer to provide a commercial ecosystem around the framework.
One of the other answers is really close to the specific use case in question but misses the point about it being SPA.
Firstly you must add your IProfileService implementation like suggested already:
public class MyProfileService : IProfileService
{
public MyProfileService()
{ }
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//get role claims from ClaimsPrincipal
var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
//add your role claims
context.IssuedClaims.AddRange(roleClaims);
return Task.CompletedTask;
}
public Task IsActiveAsync(IsActiveContext context)
{
// await base.IsActiveAsync(context);
return Task.CompletedTask;
}
}
But then go ahead and do this:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>()
.AddProfileService<MyProfileService>();
And your claim will be exposed on the JWT. Replace the ClaimTypes.Role constant with any string corresponding to the claim type you want to expose.
On Identity Server side , you can create Profile Service to make IDS4 include role
claim when issuing tokens .
You can get role claims from ClaimsPrincipal or get the roles from database and create profile service like :
public class MyProfileService : IProfileService
{
public MyProfileService()
{ }
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//get role claims from ClaimsPrincipal
var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
//add your role claims
context.IssuedClaims.AddRange(roleClaims);
return Task.CompletedTask;
}
public Task IsActiveAsync(IsActiveContext context)
{
// await base.IsActiveAsync(context);
return Task.CompletedTask;
}
}
And register in Startup.cs:
services.AddTransient<IProfileService, MyProfileService>();
On client side , you should map the role claim from your JWT Token and try below config in AddOpenIdConnect
middleware :
options.ClaimActions.MapJsonKey("role", "role", "role");
options.TokenValidationParameters.RoleClaimType = "role";
Then your api could validate the access token and authorize with role policy .
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