Documentation on How to Support Roles using ASP.Net Core 2.0 MVC and IdentityServer4 #1786
Comments
You might want to watch this: https://www.youtube.com/watch?v=EJeZ3YNnqz8 |
Not what I was asking for. I need to add Identity Roles to the claims on the User so that I can authorize, either using an Authorize(Roles="..."] attribute or an Authorization Policy that take the User roles or role claims into account. I agree that Role base authorization can be problematic, however all I need is a couple of different User types, much like the Doctor, Nurse, Patient example in the video. This can be handled by the Asp.Net Identity User Roles. Since the IdentityServer is configured to use EF and Asp.Net Identity, it should be possible to get the roles and return them to the MVC app and persist them in the identity cookie so that the User property can check for IsInRole("..."). It looks like that when the user first logs in, the claims seen by the OnUserInformationReceived Event has the roles, but subsequent requests do not have the roles. Would it be better to use a Reference token and get the information on each request? |
I didn't close this, at least intentionally. |
So then there is this bit of info as well, which might be part of your problem: https://leastprivilege.com/2016/08/21/why-does-my-authorize-attribute-not-work/ Other then these bits of info, this seems like a problem in your ASP.NET Core app, and not specifically an IdentityServer bug. Do you agree? |
Ok, I found the missing piece. The Microsoft OpenId Middleware does not map the The following code
internal class RoleClaimAction : ClaimAction
{
public RoleClaimAction()
:base("role", ClaimValueTypes.String)
{
}
public override void Run(JObject userData, ClaimsIdentity identity, string issuer)
{
var tokens = userData.SelectTokens("role");
IEnumerable<string> roles;
foreach (var token in tokens)
{
if (token is JArray )
{
var jarray = token as JArray;
roles = jarray.Values<string>();
}
else
roles = new string[]{ token.Value<string>() };
foreach (var role in roles)
{
Claim claim = new Claim("role", role, ValueType, issuer);
if (!identity.HasClaim(c => c.Subject == claim.Subject
&& c.Value == claim.Value))
{
identity.AddClaim(claim);
}
}
}
}
} You add this in the Startup of the Client project .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = identityUrl;
options.MetadataAddress = "http://identity/.well-known/openid-configuration";
options.RequireHttpsMetadata = false;
options.ClientId = clientId;
options.ClientSecret = clientSecret;
options.ClaimActions.Add(new RoleClaimAction()); // <-- add this
options.ResponseType = "code id_token";
options.Scope.Add("offline_access");
options.Scope.Add("roles");// <-- add this
options.GetClaimsFromUserInfoEndpoint = true;// <-- add this
options.SaveTokens = true;
options.TokenValidationParameters.NameClaimType = "name";// <-- add this
options.TokenValidationParameters.RoleClaimType = "role";// <-- add this
}); I'm going to look at wrapping this up so I could
|
I notice that you used to have a Roles Resource/Scope, but removed it when refactoring to Resource based configuration. I know that you dislike Role based Authorization, but you did say in the video that Identity type Roles (Doctor, Nurse, Patient) were acceptable. Since Asp.Net Core Identity already has Roles, removing support for them was probably not a good idea, at least without documenting the steps to add them back. In may cases, using roles, along with other claims is a perfectly valid design decision, given that the underlying User store already provide this so there should be no need to go through the several days of digging to make it work. |
Our docs are part of the OSS, so if you'd like to contribute to them to improve them for the community, feel free to send a PR. |
This seems to be a general question about IdentityServer - not a bug report or an issue. Please use one of the our free or commercial support options See here for more details. Thanks! |
this is exactly what i was looking for. thanks @matthewDDennis .. obviously they not going to disclose everything because they are looking for people to be get stucked and get back to them with commercial support option |
You're getting paid by your employer to write your software, yes? Then why should you expect us to do work for you for free? You're already getting this FOSS that would otherwise cost your company money. Please stop with the childish attitude. |
Thank you very much @matthewDDennis I had this issue on monday and your solution helped me out of it. Thanks. But do I have to do this for every custom claim I would want to map to the user information i.e a class for every claim? or I can have it all in one class? The reason I'm asking is Good work again. Many thanks. |
Hi! Simpler solution found here: Just use : Like :
|
roles, roles, roles |
For others that find this... As of today, if you are using Asp .Net Identity and have a role store DbContext registered in your IdentityServer4 project. You can put the claim type either in the Once that is done, the role claim will be in your access token.
|
Hmm, for me, as of today using .NET Core 3.1 it seems like this just works as expected once you implement an IProfileService and do the right configuration in Startup.cs. Not sure if it isn't actually working as I expected but I did tests and the Authorize attribute seems to work in both the AND and OR configurations when used with multiple role claims. public class ProfileService : IProfileService
{
protected UserManager mUserManager;
public ProfileService(UserManager userManager)
{
mUserManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
User user = await mUserManager.GetUserAsync(context.Subject);
IList<string> roles = await mUserManager.GetRolesAsync(user);
IList<Claim> roleClaims = new List<Claim>();
foreach (string role in roles)
{
roleClaims.Add(new Claim(JwtClaimTypes.Role, role));
}
context.IssuedClaims.AddRange(roleClaims);
}
public Task IsActiveAsync(IsActiveContext context)
{
return Task.CompletedTask;
}
} A single role produces this in the token: Multiple roles produces this in the token: All of these combinations of attributes seemed to work as expected: OR relationship [Authorize(Roles = "SystemAdmin,RegisteredUser")] AND relationship [Authorize(Roles = "SystemAdmin")]
[Authorize(Roles = "RegisteredUser")] Authentication required but no roles check [Authorize] Relevant part of Startup.cs ...
services.AddDefaultIdentity<User>(options => options.User.RequireUniqueEmail = true)
.AddRoles<IdentityRole>()
.AddRoleStore<RoleStore<IdentityRole, DbContext, string>>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddUserStore<UserStore>()
.AddUserManager<UserManager>();
services.AddIdentityServer()
.AddApiAuthorization<User, DbContext>(opt =>
{
foreach (Client c in opt.Clients)
c.AccessTokenLifetime = securityConfig.AccessTokenLifetime;
});
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddTransient<IProfileService, ProfileService>();
... Who knows, maybe it's working for a reason I don't fully understand still. |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
I have been Googling and reading the code but am still unable to figure out the necessary steps to use Role Authorization in MVC using IdentityServer4 V2 as the token server. Could you please explain how to do this in step by step detail. The are number of different answers that appear to have parts of the answer, but nothing that seems to work.
I have
UserClaims = new[] { JwtClaimTypes.Role, ClaimTypes.Role }
and it shows up in the discovery document is thescopes_supported
with the claims show up in theclaims_supported
.ProfileService
to add the "role" claims. When it is triggered by a login from the MVC app, inGetProfileDataAsync
thecontext.Subject.Claims
includes the "role" claim with a value of admin for my test user. `'context.Please provide and example or Gist that shows all the pieces and steps to allow an MVC app using IdentityServer4 V2 configured to use ASP.NET Core Identity and EntityFramework to be able to use [Authorix(Roles="admin"] attributes.
The text was updated successfully, but these errors were encountered: