1

I want to improve my API's security with some sort of "self" policy to validate the call to some user actions (like DELETE user) is made by the same user the token was issued to. Is there a way to do this in a similar way to the policy based authorization?

I have a .Net Core 2.2 with MVC WebAPI running on Kestrel. I have users, roles and user-roles and I have token-based authentication with roles enabled. I can issue tokens and validate then with the "Authorize" attribute in the controllers. However, I've been looking for a way to validate that some actions to users are made only by the users itself, a "self" authentication policy to validate that, for example, user 3 is trying to delete user 3 and only user 3. I've dug up to the claims and everything and I know I can make a simple service passing the claims and the validating it but I wanted to do it in a smoother way similar to the policy-based or role-based authentication. I don't know if I can make it with some sort of middleware or something but it would be great to be able to make it as clean as possible.

[Edit]

The main purpose is to avoid users to delete resources created by other users and make them be able only to delete resources created by themselves.

[Edit2 - Solution] Thanks to Paul Lorica's Answer I can now describe how I did it.

The first thing is to create a Requirement and a Handler similar to the examples provided by Microsoft in the docs. What we do is to add a Claim to the token generation method/service we have and add the ID as NameIdentifier. After that, we inject in the IHttpContextAccessor in the handler. And then we can validate if the ID in the request is the same than the Id in the Claim. So it was very easy.

I'm adding examples of logic to make it work.

PS: Inject IHttpContextAccessor as a singleton in the startup clas or it won't work.

Handler:

public class SelfUserHandler: AuthorizationHandler<SelfUserRequirement>
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        public SelfUserHandler(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                       SelfUserRequirement requirement)
        {
            if (!context.User.HasClaim(c => c.Type == ClaimTypes.NameIdentifier))
            {
                return Task.CompletedTask;
            }

            var nameIdentifier = context.User.FindFirst(c => c.Type == ClaimTypes.NameIdentifier).Value;

            if (_httpContextAccessor.HttpContext.Request.Path.ToString().ToUpper().Contains(nameIdentifier.ToUpper()))
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }



            return Task.CompletedTask;
        }
    }

Requirement

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

Additional info: Nate Barbettini Answer here Joe Audette Answer here

  • Can you be a little more elaborate on your example : "user 3 is trying to delete user 3 and only user 3" ? The user can only delete a resource that was created by himself? – Ram Kumaran Aug 18 '19 at 17:04
  • @RamKumaran yes. User can only delete resources created by itself. That's the main purpose, to avoid users deleting resources not created by themselves. – Carlos Draper Giggs Aug 18 '19 at 18:11
  • Simply, add logic to your business layer to check the logged in user, and determine whether they can "do" the action you want. – Robert Perry Aug 19 '19 at 09:36

1 Answers1

0

First off, when your code validates against the policy, the policy has no understanding, and does not need to know, what you are doing.

I suppose you can retrieve the context via URL. So say if its a DELETE user/3

then you can create a policy that would check the user's claims that it has an ID == 3.

See the docs here on creating policies and accessing the httpContext

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

Its a bit of a naive check, I would rather just place that logic within the method of the controller.

Paul Lorica
  • 703
  • 4
  • 6
  • Thanks, this seems pretty much it. I can get the ID from the "NameIdentifier" claim, and I'm using this policy with a blank requirement because I don't know how to pass the "3" from the request DELETE user/3 to the policy requirement. So, the question now is: is there a way to pass the Id to the policy so I can verify against the Token Claims? or as a parameter to its requirement? – Carlos Draper Giggs Aug 19 '19 at 17:28
  • Ok, thanks a lot for the idea. I ended up doing a Requirement and a Handler and I injected IHttpContextAccessor to the handler and there I can get the ID from the request and check if the nameIdenfier is the same to the Id from the request. Pretty much it. I will put all the code in the question so it's all clear. Thanks!! – Carlos Draper Giggs Aug 19 '19 at 18:01