Reputation: 7996
I want to implement multiple role-based authorizations with IdentityServer4 hybrid, everything is fine but when I want to use like this:
[Authorize(Roles = "Admin,SalaryUser")]
it doesn't allow me and give access denied.
In my scenario, users have multiple roles and if a role is valid the controller should give me access, for example in the above code the controller should give access to these users: the users have the SalaryUser role, the users with admin roles, the users have both Admin, SalaryUser roles.
here is configuration:
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = authority;
options.RequireHttpsMetadata = false;
options.ClientId = clientId;
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.UseTokenLifetime = false;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.MapCustomJson("role", jobj =>
{
IEnumerable<string> values = jobj["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"].Values<string>();
StringBuilder sb = new StringBuilder();
foreach (string val in values)
{
sb.Append(val + ",");
}
return sb.ToString().TrimEnd(',');
});
options.Scope.Add("api1");
options.Scope.Add("offline_access");
// options.Scope.Add("roles");
options.Events = new OpenIdConnectEvents()
{
OnUserInformationReceived = async UserInformationReceivedContext =>
{
// UserInformationReceivedContext.User.Remove("address");
if (UserInformationReceivedContext.User.TryGetValue("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", out JToken role))
{
var claims = new List<Claim>();
if (role.Type != JTokenType.Array)
{
claims.Add(new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", (string)role));
}
else
{
foreach (var r in role)
claims.Add(new Claim("role", (string)r));
}
var id = UserInformationReceivedContext.Principal.Identity as ClaimsIdentity;
id.AddClaims(claims);
}
}
};
options.ClaimActions.MapAll();
});
Upvotes: 1
Views: 2107
Reputation: 27528
You don't need to manually map claims with MapCustomJson
or in your OnUserInformationReceived
.
If the claim in jwt token is role
not http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role
, you can set client app's role validation claim :
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = "role"
};
Another way is when adding role claim to your issued token on identity server application , use ClaimTypes.Role
:
new Claim(ClaimTypes.Role, "Admin"),
Upvotes: 1
Reputation: 3177
You need to map the JWT claim type to the claim type that Identity uses.
The issue at hand is that the identity system for ASP.NET Core relies on
the ClaimTypes.Role
constant
(http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role) to
determine the roles of the user. However, the name of the claim that
corresponds to the role on an OpenID JWT token is simply role
. Any
time you need to merge OIDC identity and ASP.NET identity, you have to translate
claims like this.
I rewrote your code a little bit because you can work with the claims identity without using the JTokenType. Although I didn't have time to test this thoroughly so if it doesn't then stick to your code but replace "role" with ClaimTypes.Role when you add claims to the identity.
OpenIdConnectEvents CreateOpenIdConnectEvents()
{
return new OpenIdConnectEvents()
{
OnTicketReceived = context =>
{
var identity = context.Principal.Identity as ClaimsIdentity;
if (identity != null)
{
if (identity.HasClaim(c => c.Type == "role"))
{
foreach (var role in identity.Claims.Where(c => c.Type == "role"))
{
if (!context.Principal.HasClaim(c => c.Type == ClaimTypes.Role && c.Value == role.Value))
{
identity.AddClaim(new Claim(ClaimTypes.Role, role.Value));
}
}
}
}
return Task.FromResult(0);
}
};
}
Use like this:
options.Events = new CreateOpenIdConnectEvents()
Upvotes: 0