1

I have been implementing my own JWT Token Authentication/Authorisation, without using Identity etc.

Based on these links I have successfully created a JWT Token - and implemented the Authorisation using the Attribute [Authorize]

Token Authentication 1

Token Authentication 2 - [My Code is exactly the same as this]

The problem I am having is that in the application I am working on, the Authorisation is based on FEATURE not ROLE.

This is the DB Schema

1. User
-------------------
   UserId (PK)

2. Role 
-------------------
   RoleId (PK)


3. RoleFeatures
-------------------
   (RoleId PK, FK)
   (FeatureId PK, FK)

4. Features
-------------------
   FeatureId (PK)

The Roles are NOT pre-defined but Features are pre-defined e.g. Create User, Update User

Let's say we have 2 Roles "User Manager" and "User and Customer Admin" - The users from both these roles can Create, Update and Delete Users.

Now in the Application Controller instead of doing

   [Authorise(Roles="User Manager, User and Customer Admin")]
   public IActionResult CreateUser()

I need this:

   [Authorise(Features="Create User")]
   public IActionResult CreateUser()

How do I come about implementing this type of JWT Token Authorization in MVC Core Web API?

One way to do it would be to save the Features in the JWT Token as a Claim - but the problem with that is that a Role can have say 20 features, and saving them in the Token will increase the Token Size dramatically -

So what I need is a Custom Implementation of the Authorize Attribute which get's the userId from the JWT Token (userId is Claim in JWT Token), then get's the Role Features from the DB based on UserId and apply's the Authorization.

EDIT

Based on the .NET Core Documentation - I found that I we can create a Policy

https://docs.asp.net/en/latest/security/authorization/policies.html

So I went ahead and created a Requirement and a Requirement Handler as follows:

public class FeatureRequirement : IAuthorizationRequirement
{
    public string FeatureName { get; set; }

    public FeatureRequirement(string featureName) { FeatureName = featureName; }
}

public class FeatureRequirementHandler : AuthorizationHandler<FeatureRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FeatureRequirement requirement)
    {
        var userId = context.User.Claims.FirstOrDefault(s => s.Type == "UserId");

        if (userId == null) { return Task.FromResult(0); }

        //TODO: Get User Features from DB based on UserId
        var userFeatures = new List<string> { "Create User", "Update User" };

        if (userFeatures.Contains(requirement.FeatureName))
        {
            context.Succeed(requirement);
        }
        return Task.FromResult(0);
    }
}

and in the Startup.cs

  services.AddAuthorization(options =>
     {
      // Create the Policies
      // Problem here have to define a policy for each feature 

       options.AddPolicy("Create User", policy => policy.Requirements.Add(new FeatureRequirement("Create User")));

       options.AddPolicy("Update User", policy => policy.Requirements.Add(new FeatureRequirement("Update User")));

     });

    services.AddScoped<IAuthorizationHandler, FeatureRequirementHandler>();

and then in the Controller:

   [Authorise(Policy="Create User")]
   public IActionResult CreateUser()

   // AND

   [Authorise(Policy="Update User")]
   public IActionResult UpdateUser()

Which is working as expected (returns 403 if feature is not assigned to user)

The Problem with this is that I have to create a new Policy for each and every single Feature - is there a better way to do this?

Dawood Awan
  • 6,052
  • 10
  • 44
  • 109
  • See http://stackoverflow.com/questions/36445780/how-to-implement-permission-based-access-control-with-asp-net-core , especially read comments. – adem caglin Sep 26 '16 at 10:25
  • @ademcaglin seems like my EDIT is the way to go then? Will have to register multiple Policies in the Startup.cs using a Loop... – Dawood Awan Sep 26 '16 at 10:30

0 Answers0