IsInRole return false even if there is role in claims

If your service is using Windows authentication, then the IPrincipal.Identity you receive will be of type WindowsPrincipal. It's a little misleading, but the ClaimType that WindowsPrincipal.IsInRole() looks for is not ClaimTypes.Role as you might reasonably expect, but ClaimTypes.GroupSid.

However, you should not assume the actual ClaimType that the current Identity uses for specifying roles because different types of identity use different values. Instead you should reference the ClaimsIdentity.RoleClaimType property.

We have implemented a IAuthenticationFilter along the following lines:

public Task AuthenticateAsync(HttpAuthenticationContext context, cancellationToken)
{
    var principal = context.Principal;

    if(principal.Identity is ClaimsIdentity && principal.Identity.IsAuthenticated)
    {
        var ci = (ClaimsIdentity)principal.Identity;
        // get the user's additional roles from somewhere and add Claims
        ci.AddClaim(new Claim(ci.RoleClaimType, "MyRole"));
    }
}

This allows us to use the standard AuthorizeAttribute mechanism in our ASP.Net Controllers. e.g.

[Authorize(Roles="MyRole")]
public IHttpActionResult Get()
{
    //authenticated and authorised code here
}

See ClaimsIdentity.RoleClaimType on MSDN for further clarification.

Please note: adding user-defined roles to the WindowsPrincipal can cause problems. It seems that the current implementation of .Net Framework 4.5 (as of April 2017) will sometimes throw an exception when checking roles, expecting the details of the role to be available from Active Directory. See this question for an alternative approach.


Probably, the ClaimType of the claim is just "role".

You should create the claim using Microsoft Schema:

manager.AddClaim(dn1.Id, claim: new Claim(ClaimTypes.Role.ToString(), "ADMINISTRATOR"));

Then User.IsInRole("Admin"); and [Authorize (Roles = "Admin")]will work properly.

This because Microsoft Identity uses the schema:

http://schemas.microsoft.com/ws/2008/06/identity/claims/role

When for role checking. I suggest you to check ASPNETIdentity database to have a complete view of how che claim are inserted. I'm pretty sure that the ClaimType of AspNetUserClaims is not like the Microsoft Schema.

Regards


TL;DR Case Sensitivity, Perhaps?

I found that the check used by default in...

  [Authorize(Roles = "RoleA,RoleB")] 

...was case sensitive.

I created roles in mixed case, and used AspNetCore's Identity manager, with an non-EF memory implementation for testing.
UserManager.IsInRole("RoleA") returned true, but when checked via the ClaimsPrincipal, HttpContext.User.IsInRole("RoleA") returned false. I dumped the claims out to text and could see that there were role claims for the correct MS schema...

    ClaimType:[http://schemas.microsoft.com/ws/2008/06/identity/claims/role], ClaimValue:[ROLEA], Issuer:[TokenServer]
    ClaimType:[http://schemas.microsoft.com/ws/2008/06/identity/claims/role], ClaimValue:[ROLEB], Issuer:[TokenServer]

...but the claim value (the role) was upper case.
To fix the problem, I just had to change the attribute to...

[Authorize(Roles = "ROLEA,ROLEB")]

... and it worked.

So, if you are having a problem getting roles authorization to work in AspNetCore, try to read the claims, and match the claims exactly. You can read the claims by accessing the HttpContext.User.Claims object...

        foreach (var claim in HttpContext.User.Claims)            
            Console.WriteLine($"ClaimType:[{claim.Type}], ClaimValue:[{claim.Value}], Issuer:[{claim.Issuer}]");

It could of course be that I somehow donkey-coded the roles to upper case, or somewhere used the NormalisedRole, but you might have done the same thing...