1

I just implemented a web api using claims-based authorization. A user can login in the system and a set of claims are pulled from the database and added to the httpContext.User.Identity depending on what the user can do.

After registering the policies in Startup.cs with something like:

services.AddAuthorization(options =>
{
    options.AddPolicy(PoliciesDefinitions.RequiresVehicleList, policy => policy.RequireClaim(Permissions.VehiclesList.ToString()));

    ...               
});

I can use the Authorize attribute on the controllers method that I want to authorize with something like:

Authorize(Policy=PoliciesDefinitions.RequiresDriversList)]
[HttpGet]
public ActionResult Get() { ... }

This works ok but today I was reading microsoft documentation a bit more thoroughly and I came across this statement in the Claims-based authorization documentation:

A claim is a name value pair that represents what the subject is, not what the subject can do

At this time I'm doing exactly what microsfot suggests not to do. I'm adding what the user can do (permissions) to the identity . So, this leads me to think, am I doing it wrong? If the answer is yes, where would you store the user permissions and how would authorization work?

Notbad
  • 5,936
  • 12
  • 54
  • 100

1 Answers1

1

This allows for the KVP, and multiple values.

// with Razor, you did not specific if it was core 2, 3.1 or Razor

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();

    services.AddAuthorization(options =>
    {
        options.AddPolicy("Vendors", policy =>
                          policy.RequireClaim("Type.Tykt.org", "Dealer", "Driver", "WholeSaler", "Asset", "Repair"));
    });
}

Option 2:

Also there is a claims collection, you can add it after user successfully logs in.

var user = new User {
  Email = "xyz@tykt.org",
  Name =  "xyz"
}

user.Claims.Add(new IdentityUserClaim<string> 
{ 
    ClaimType="your-type",   // your key
    ClaimValue="your-value"  // your value
});

await userManager.CreateAsync(user);

Update Ref MSDN:

Its really your choice on how you store retrieve, if I'm understanding the question, your question specifically around is the value of the claim.

Typically, the mapping and verification happens in something like a PermissionHandler : IAuthorizationHandler or a generic approach MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>. Which, loads the values, and handles the requirement verification of a specific permission for e.g. min age, but the actual claims (what are you stating/min age policy vs the value is usually in the DB, like DOB=1/1/1990) travel with the Principal object. Now, where you choose to retrieve the value of the claim is upto you

In the below function he is getting value for the Key from the Context and then validating

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                        c.Issuer == "http://contoso.com"))
        {
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(
            // He gets the value on the server-side from any store or 3rd party relayer
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth && 
                                        c.Issuer == "http://contoso.com").Value);

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}
Transformer
  • 6,963
  • 2
  • 26
  • 52
  • Well, As I explained I'm using option 2. After the user logs in I am filling the user Claim collection of httpContext.User.Identity with the list of permissions the user is granted. But as I said in the microsoft documents it states that claims should not be used to define what the user can do. By the way I use .net core 3.1. My main concern is, if microsoft says I shouldn't be filling the identity claims collection with the user permissions, where should I store them after a succesfull login? Should I continually pull them out from the db when needed? – Notbad Apr 13 '20 at 22:31
  • Here is another answer showing that he stores it with [usermanager or its own table](https://stackoverflow.com/a/39662673/6085193) The answer above shows how he handles the key and the value – Transformer Apr 13 '20 at 23:18
  • 1
    Then I do it correctly. Thanks. As I seed I store it in the db and fill up the user identity with them when the user logs in. I think I was confused with that microsoft statement. – Notbad Apr 14 '20 at 10:20
  • I know right, claims was confusing to me as well. I simply wish they had `groups` or something had a `cross cutting` function `across roles`. – Transformer Apr 14 '20 at 20:45