24

I understand that I can use claims to make statements about a user:

var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, "Peter"));
claims.Add(new Claim(ClaimTypes.Email, "peter@domain.com"));

But how should I store "role-based" claims? For example:

The user is a super administrator.

claims.Add(new Claim("IsSuperAdmin, "true"));

The value parameter "true" feels completely redundant. How else can this statement be expressed using claims?

Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
Dave New
  • 38,496
  • 59
  • 215
  • 394

3 Answers3

38

This is already done for you by the framework. When user is logged in, all user roles are added as claims with claims type being ClaimTypes.Role and values are role name.

And when you execute IPrincipal.IsInRole("SuperAdmin") the framework actually checks if the claim with type ClaimTypes.Role and value SuperAdmin is present on the user.

So don't need to do anything special. Just add a user to a role.

trailmax
  • 34,305
  • 22
  • 140
  • 234
  • 1
    What is the relation between Claims of type Role and AspNetRoles table? – tymtam Jun 21 '16 at 01:35
  • 1
    @Tymski `AspNetRoles` contains list of roles. When user logs in, all these roles are added into the cookie as claims of type `ClaimTypes.Role`. Claims in cookie are short-lived. Records in database are just copied into the cookie. – trailmax Jun 21 '16 at 09:05
  • 4
    I should point out that `IPrincipal.IsInRole("xx")` does not necessarily use `ClaimTypes.Role` when searching for a matching claim. For example the `WindowsPrincipal` you may receive after doing Windows authentication actually uses the `ClaimTypes.GroupSid` for specifying roles. Instead use the `ClaimsIdentity.RoleClaimType` property. – Rob Oct 14 '16 at 14:54
  • 1
    @Rob Context is king. `IsInRole` is part if `IPrincipal` interface (https://referencesource.microsoft.com/#mscorlib/system/security/principal/iprincipal.cs,c78f7d29e063c03c,references) and whatever object implements it can do whatever it does. Here we talk about `ClaimsPrincipal` (https://referencesource.microsoft.com/#mscorlib/system/security/claims/ClaimsPrincipal.cs,765) And it uses Claims to define roles. If you happen to get `WindowsPrincipal` in Identity framework inside your MVC app, you are doing something very wrong. – trailmax Oct 14 '16 at 15:36
  • 1
    @trailmax `WindowsPrincipal` derives from `ClaimsPrincipal` in .Net 4.5, and I believe you'll find that `WindowsPrincipal` is what you will receive in an ASP.Net Web API (and MVC?) if you configure the application to use windows integrated authentication. But the point here is that using the specific `ClaimTypes.Role` to drive `ClaimsPrincipal.IsInRole` is not correct. See [ClaimsPrincipal.IsInRole](https://msdn.microsoft.com/en-us/library/system.security.claims.claimsprincipal.isinrole(v=vs.110).aspx) – Rob Oct 14 '16 at 16:50
  • 1
    @Rob fair enough. I just followed through the source code to this line: https://referencesource.microsoft.com/#mscorlib/system/security/claims/ClaimsIdentity.cs,72 and hence my reference. – trailmax Oct 14 '16 at 17:35
  • 1
    @Rob though, if you look through [ClaimsPrincipal.IsInRole](https://referencesource.microsoft.com/#mscorlib/system/security/claims/ClaimsIdentity.cs,583) it will lead you to the same constant, pointing to `ClaimsTypes.Role`. But I'm not sure why there is a need to reference the same constant from different properties. Perhaps backwards compatibility. – trailmax Oct 14 '16 at 17:38
  • 3
    Hi @trailmax. It's a little esoteric but note that [ClaimsPrincipal.IsInRole()](https://referencesource.microsoft.com/#mscorlib/system/security/claims/ClaimsPrincipal.cs,771) uses the property `RoleClaimType` to test if the contained identity has the required claim. You can see in the reference source for ClaimsIdentity that the backing field for this property defaults to `ClaimsType.Role` but the `WindowsIdentity` constructor passes `ClaimTypes.GroupSid` for this field. I don't the underlying reason, but this is why it is best to use the `RoleClaimType` property. – Rob Oct 17 '16 at 10:27
  • @Rob agreed, one should not be using internal/hidden APIs. But it is good to know what happens under the hood. – trailmax Oct 17 '16 at 10:30
21

You can store roles using the ClaimType Role

claims.Add(new Claim(ClaimTypes.Role, "SuperAdmin"));
JCS
  • 1,071
  • 2
  • 9
  • 24
3

You need to specify the Role in a claim with a type of ClaimsType.Role and then specify the claim type that contains the role in the ClaimsIdentity as shown below.

var claimsIdentity = new ClaimsIdentity(new[]
{
    new Claim(ClaimTypes.Email, "peter@domain.com"),
    new Claim(ClaimTypes.Name, "Peter"),
    new Claim(ClaimTypes.Role, "SuperAdmin"),
},
"ApplicationCookie", ClaimTypes.Email, ClaimTypes.Role);

This will then allow you to use the [Authorize(Roles = "SuperAdmin")] attribute in your controllers.

Macey41
  • 31
  • 2