chobo2
chobo2

Reputation: 85725

Is Claims Better Way to go rather than Roles

I am building a website(front end reactjs, backend asp.net web api core 2) and trying to figure how to do authentication/authorization.

For authentication I pretty much will JWTBearer tokens, if the username and password match what I got for the user in my db, give them a token.

It is for authorization that I am not sure about, The last time I had to do something it was more have roles in your database and then check if that user had that role or not when they tried to access something.

Now I was looking at this blog and the author talks about

Rather than try and store all the “roles” that a user might have (e.g. administrator, user, super user) you can store information about the user as claims.

I am confused about this, how does this work. Say I need one user to be able to see the very secret area of my site(ie admin area) but another user who is also logged in can't see it as he is a general user.

What information is used to make this claim? What is being stored in the database?

With roles, you could have something like User can have a Roles (ie Admin) and then just check that when they try to do something.

I am also wondering how would the front end know what to hide(ie the link that says "admin area") as can you send back a result saying that they have a claim that allows them to see the admin area?

Upvotes: 2

Views: 2638

Answers (2)

Eric Patrick
Eric Patrick

Reputation: 2247

Claims can be used to manage a wider array of use cases than Role, particularly when paired with Policies and Requirements. Two examples that come to mind:

  1. Requiring a user have a specific reputation in order to edit a record
  2. Requiring a user to be at least 21 to order a particular product

One could create an "Over21" role, and a "Over1000Reputation" role, but maintaining role membership becomes unwieldy; you need to set up jobs to add (or remove) users from those roles as their age or reputation changes.

Instead, have your JWT include a Reputation and BirthDate claims:

identity.AddClaim(new Claim("Reputation", 722));
identity.AddClaim(new Claim("BirthDate", new DateTime(2000, 1, 1)));

Your code could then implement a Requirement and AuthorizationHandler that calculates whether the user should be authorized based on these claim values:

public class ReputationRequirement : IAuthorizationRequirement
{
    public int Reputation { get; private set; }

    public ReputationRequirement (int reputation)
    {
        Reputation = reputation;
    }
}

Paired with an AuthorizationHandler along these lines:

public class ReputationHandler : AuthorizationHandler<ReputationRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ReputationRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == "Reputation"))
        {
            return Task.CompletedTask;
        }

        var reputation = context.User.FindFirst(c => c.Type == "Reputation").Value as int;
        if (reputation >= requirement.Reputation)  
            context.Succeed(requirement);
        return Task.CompletedTask;
    }
}

In your startup's ConfigurationServices, you can give a name (Policy) to a particular instance of your requirement:

services.AddAuthorization(options =>
{
    options.AddPolicy("Reputation750", policy => policy.Requirements.Add(new ReputationRequirement(750)));
    options.AddPolicy("OldAndWise", policy => 
    {
        policy.Requirements.Add(new ReputationRequirement(750));
        policy.Requirements.Add(new MinimumAgeRequirement(40));
    }
});

Lastly, you can mark MVC controllers or methods with a policy:

[Authorize(Policy = "OldAndWise")]
public class StackOverflowWineController : Controller
{
    // omitted for brevity
}

Note the example linked to has more detail that this brief code snippet.

Regarding storage of claims in a database, that depends on the use case. When treating Roles as Claims, you would still have something like a UserRoles and RoleMembership tables. For Reptuation and BirthDate, it would probably make more sense to store them in a User table, rather than some sort of dedicated generic Claims table.

Upvotes: 4

Neville Nazerane
Neville Nazerane

Reputation: 7019

Roles can still be used like in the older versions. Claims are simply a way to store more user data and provide more flexibilities with the authentication. You can add them to a list like this:

List<Claim> claims = new List<Claim> {
    new Claim(ClaimTypes.Name, "username"),
    new Claim(ClaimTypes.Role, "First Role"),
    new Claim(ClaimTypes.Role, "Second Role"),
    new Claim(ClaimTypes.Role, "Third Role")
};

You can authorize any controller or action using [Authorize(Roles = "user, superUser")]. If you want to display something to a user based on roles, you can use @if (User.IsInRole("User")).

As of JWT creation, you can have a look at this class: https://github.com/neville-nazerane/netcore-jwt-sample/blob/master/website/TokenGenerator.cs

It has utility functions to create a JWT with a given user name and also one that takes in a username and a comma separated string of roles.

Upvotes: 0

Related Questions