Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET core Web API: Authorization based on permissions from database

I am looking for a solution/suggestion that helps me creating permission based access to web api endpoints/controller actions.

Role based access is not suitable becuase I don't have fixed rules that I could use in code like Role("Admin") oder Role("Controller").

Claim based permissions is also not feasable because each user/client can have different permissions on each business object/entity (e.g. Read/Write-access to own tickets and read access to all ticket of his/her company or if its a technician of my company full access to all tickets of all customers. So each user would have 10s or even hundrets of claims which I would have to evaluate at each access of my API.

It is some kind of multi tenancy in just on database and the tenants are our customers with some kind of "master tenant" that has access to all of the tenant data.

I think that something like Visual Guard would satisfy my needs but it is pretty expensive and they don't support net core for now and their documentation seems pretty outdated.

I don't need a usable solution at once but some hints and tricks how I could achieve that would very much be apprieciated because I am looking and searching for some time now.

Details on "database permissions": What I mean is in my frontend (Winforms app) I want to establish a security system where I can create and assign roles to users and in those roles is defined which actions a user can execute and which CRUD operations he/she can do on specific business objects. Each role can have n users and each role can have n permissions. Each permission on itself declares for exmaple Create:false, Read:true, Write:true and Delete:false. If a permission for a specific business object is not found CRUDs on that BO is denied totally.

So whenever an action in my API is called I have to check if that user and his/her rule allows him to do that specific action based on rules and permissions in my database.

Details an application structure: Frontend will be a Winforms app which calls the API in the background by OData. I don't want to rely solely on security in the Winforms app because the API will be accessible from the internet and I can't be sure if a user would not try to access the api with his credentials just to see what is possblie without the "frontend filter". So the permissions lie in the API and if a user tries to access s.t. in the frontend app the app itself "asks" the API if that is possible. Later on I want to create mobile clients that also use the Odata Web API.

like image 986
El Hippo Avatar asked Mar 31 '19 14:03

El Hippo


People also ask

How will you implement authentication and authorization in ASP.NET web API?

Web API assumes that authentication happens in the host. For web-hosting, the host is IIS, which uses HTTP modules for authentication. You can configure your project to use any of the authentication modules built in to IIS or ASP.NET, or write your own HTTP module to perform custom authentication.

What is difference between authentication and authorization in ASP.NET Core?

Authentication is the process of determining a user's identity. Authorization is the process of determining whether a user has access to a resource. In ASP.NET Core, authentication is handled by the authentication service, IAuthenticationService, which is used by authentication middleware.


1 Answers

The relevant API in asp.net core are:

  • IAuthorizationService
  • AuthorizationPolicy
  • IAuhtorizationRequirement
  • IAuthorizationHandler

The authorization pattern you are looking for is called Resource-based authorization

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

Basically, you can define AuthorizationPolicy, and apply it to a instance of a resource:

var ticket = _ticketRepository.Find(ticketID);

var authorizationResult = await _authorizationService
        .AuthorizeAsync(User, ticket, "EditTicketPolicy");

In the authorization handler, you can check if the user is the owner of the resource.

public class ResourceOwnerRequirement : IAuthorizationRequirement
{
}

public class ResourceOwnerHandler 
    : AuthorizationHandler<ResourceOwnerRequirement, MyBusinessObject>
    //: AuthorizationHandler<ResourceOwnerRequirement> use this overload to handle all types of resources...
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, 
        ResourceOwnerRequirement requirement, 
        MyBusinessObject resource)
    {
        int createdByUserId = resource.CreatedBy;
        Claim userIdClaim = ((ClaimsIdentity)context.User.Identity).FindFirst("UserId");

        if (int.TryParse(userIdClaim.Value, out int userId)
            && createdByUserId == userId)
        {
            context.Succeed(requirement);
        }
    }
}

//admin can do anything
public class AdminRequirementHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        if (context.User.Claims.Any(c => c.Type == "Role" && c.Value == "Administator"))
        {
            while (context.PendingRequirements.Any())
            {
                context.Succeed(context.PendingRequirements.First());
            }
        }
        return Task.CompletedTask;
    }
}

BTW, this still can be called claims or role based authorization. Users with specific role can edit their own tickets, but users with admin role also other tickets. The difference is that you apply authorization to a resource, not just action

EDIT:

like image 91
Liero Avatar answered Oct 23 '22 03:10

Liero