3

My goal is to authorize users only if the current logged on user's customerId matches the customerId on the url/controller.

I'm using ASP.NET Core 2.0 with entity framework. I have extended ApplicationUser with a CustomerId int that has a FK to a CustomerIdentity table.

I am able to retrieve the customerId from the logged in user, using this method here: http://rion.io/2016/01/04/accessing-identity-info-using-dependency-injection-in-net-5/

I have made a routing:

routes.MapRoute(
            name: "customers",
            template: "{customerId?}/{controller=Home}/{action=Index}/{id?}");

And then in my controllers I have a customerId parameter.

Global admins have the rights to use any customerId they like, but for everyone else they can only use their customerId belonging to their logged in user.

I was thinking of using this approach to check if customerId from url, e.g. /23/Computers/Approve matches currentCustomerId

But I'm not sure how to adapt it to a policy and claim that asp.net core uses.

What I have sofar:

public class CustomerRequirement : IAuthorizationRequirement
{
    public bool IsMatchingLoggedInUser { get; private set; }

    public CustomerRequirement(bool isMatchingLoggedInUser)
    {
        IsMatchingLoggedInUser = IsMatchingLoggedInUser;
    }
}

Not sure how to make this one:

public class CustomerRequirementHandler : AuthorizationHandler<CustomerRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomerRequirement requirement)
    {
        //GetDate.CurrentCustomerId is a static int property set after user has logged in
        if (GetData.CurrentCustomerId == /*Get httpContext customerId ??? "")*/ 42) 
        {
            context.Succeed(requirement);
        }


        return Task.CompletedTask;
    }
}

Not sure if I'm totally off here or it can be done differently? I am open for suggestions.

Update (what I ended up with)

public class ValidateCustomerAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (GetData.CurrentCustomerId != (int)context.ActionArguments["customerId"])
            {
                context.Result = new UnauthorizedResult();
            }
        }
    }

Then on my controllers I decorated it with this attribute. I still need to exclude admins in this, but that will come later. (and some more errorhandling :))

Morten_564834
  • 1,479
  • 3
  • 15
  • 26

1 Answers1

1

I'm not familiar with how ASP.NET Core does this (is AuthorizationHandler similar to an action filter?), but this seems like something you can handle using roles.

I have a custom Auth action filter where I put custom validation logic like this. In the controller, you could check the roles of the logged in user. If they don't have role "Admin" and their customer ID does not match the customer ID in the URL, return a 403. Otherwise, continue as normal.

If getting the value of the customer ID is a problem for you and you don't have access to the action parameters, then you can use the current request's Uri, split the path apart and pull it out that way. But that's a fragile implementation.

Brett Allen
  • 5,297
  • 5
  • 32
  • 62
  • That is my goal yes, In mvc5 i could make a custom authorize attribute with custom logic, but here I need to use a handler where I can't seem to pass in httpcontext or other stuff. Maybe it's just me lacking knowledge about how custom handlers is suppose to act. – Morten_564834 Nov 05 '17 at 11:50
  • Ok seems like action filters are still available in core, so I'll try this approach. I was also thinking of making a custom routing constraint, so the id in the url must belong to logged in user unless it was an admin, but might be complicated because I need to ensure the user is logged on etc. – Morten_564834 Nov 05 '17 at 16:33
  • Ok I used the actionfilters, and it works fine. So thanks for the help. I have editted by question with what I implemented. Feel free to comment on it. – Morten_564834 Nov 05 '17 at 17:01
  • I still feel that the "correct" way would be to make a custom authorization claim/policy. But whatever, it works :) – Morten_564834 Nov 05 '17 at 17:08