1

I am working with Web API and generate JWT token on login. I have a very complex requirement regarding user roles and claims. A user can have multiple roles. A role can have multiple claims and a user can have particular claims. I am using Asp.Net Core Identity. One user can have data in AspNetUserClaims and same user can have different role and each role have claims in AspRoleClaims. I have successfully implemented the Role base authorization using web api but how can I implement claim based authorization in web API?

The one thing that I have already implemented, but don't really like is that I am storing all user claims including claims in the role in a JWT token and on each request I am extracting the claims from the JWT. But is this a best practice or not - Storing large data in JWT token?

Or what is the other way to do claim based authorization in Web API Dot Net core?

1 Answer 1

3

You can create custom policies like so, you can have multiple individual policies and stack them with authorize tags, or you can have one handler to deal with multiple policies, or in a policy you can have multiple requirements in the policyBuilder (I like this approach for a lot of use cases). See the info below and the microsoft link

an example for registering a custom policy:

Startup.cs

 services.AddAuthorization(authorizationOptions =>
 {
    authorizationOptions.AddPolicy(
    "MustBeBornInSummer",
    policyBuilder =>
    {
       //add any other policy requirements here too including ones by default
       //eg policyBuilder.RequireAuthenticatedUser();
        policyBuilder.AddRequirements(
            new MustBeBornInSummerRequirement()
            //, new AnotherRequirement()
            );
    });
    //only if you want to register as the default policy
    authorizationOptions.DefaultPolicy = authorizationOptions.GetPolicy("MustBeBornInSummer");
});

Then in the authorize

Tag you use

[Authorize("MustBeBornInSummer")]

You can set a default policy either which allows you to use the Authorize tag as normal

authorizationOptions.DefaultPolicy = authorizationOptions.GetPolicy("MustBeBornInSummer");

Your requirement class

public class MustBeBornInSummerRequirement : IAuthorizationRequirement
    {
        public MustBeBornInSummerRequirement ()
        {
        }
    }

Handler class

 public class MustBeBornInSummerHandler: AuthorizationHandler<MustBeBornInSummerRequirement>
    {

        public MustBeBornInSummerHandler ()
        {
            //your dependency injections
        }

        protected override Task HandleRequirementAsync(
            AuthorizationHandlerContext context, 
            MustBeBornInSummer requirement)
        {  
//Get the claim you want
            var subject = context.User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;

            var dateOfBirth = _useryRepository.GetApplicationUserProfile(subject)?.dob;


           //your logic`check user claims then do stuff, or/and check roles too
            if (!bornInSummer)
            {
                context.Fail();
                return Task.CompletedTask;
            }


            // has claims
            context.Succeed(requirement);
            return Task.CompletedTask;
        }
    }

In your configure services you need to register it:

services.AddScoped<IAuthorizationHandler, MustBeBornInSummerHandler>();

You Can have One handler for multiple requirements too see

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();
        foreach (var requirement in pendingRequirements)
        {
          if(requirementMustBeBornInSummer
             //Dostuff continue
          elseIf(requirementIsBlah)
        }
    }
}

How to handle claims is up to you, this is how I normally do it

var subject = context.User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;

var dateOfBirth = _useryRepository.GetApplicationUserProfile(subject)?.dob;


//My repository
public ApplicationUserProfile GetApplicationUserProfile(string subject)
{
    return _context.ApplicationUserProfiles.FirstOrDefault(a => a.Subject == subject);
}

you can also have individual policies and stack them

[Authorize(Policy = "MustBeBornInSummer")]
[Authorize(Policy = "readRole")]

https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.1

As for storing large amounts of data in a JWT, for application specific claims I usually create a table that stores claims for a user in a particular app. I then use the "sub" of the token to look up the claims the user has. This stops you needing to return really large tokens.

Sign up to request clarification or add additional context in comments.

10 Comments

How we add policy in claims?
I'm unsure what you are asking? if it is about where to store claims, I just updated my answer
I am using asp.net identity core.
How JWT authorize the polices or maybe i don't have clear concept about it
In the first code block I register the policy in startup, If I dont set it to be the default policy, whenever I want the policy to be used I use the following [Authorize(Policy = "MustBeBornInSummer")]`` in my controller. your handler decides whether the policy conditions are met or not. you do this by context.Fail(); return Task.CompletedTask; if the policy conditions arent met or context.Succeed(requirement); return Task.CompletedTask; if the conditions are met
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.