Search code examples
asp.net-coreasp.net-identity

.NET Core Identity API with permission based auth


I'm new at Identity API but in my web application: Institution users creates other users for own institution and and they want to decide who see this page or not.My controller methods like this ;

    [Authorize]
    public IActionResult Privacy()
    {
        return View();
    }

But also user's have permissions to do any actions like this enum and enum is bigger than 50;

    public enum PermissionTypes
    {
        UserCreate = 1,
        UserEdit = 2,
        UserDelete = 3,
        ....
    }

And i do some research and found policy based authorization but when you create a new policy you must declare at Startup.cs and its not good for me because when you do that you always publish new codes in production.What i need is something like that ;

    [CustomAuth(PermissionTypes.UserCreate)]
    public IActionResult Privacy()
    {
        return View();
    }

Is there any solution for this situation ?


Solution

  • There is many ways to do this. A lot of people recommend claims and policy based security... I personally found this approach a little "stiff".

    So instead I do this a little different:

    First create a class like this:

    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Authorization.Infrastructure;
    using Microsoft.AspNetCore.Identity;
    using System.Linq;
    using System.Security.Claims;
    using System.Threading.Tasks;
    
    namespace Bamboo.Web.CoreWebsite.Membership
    {
      public class PermissionHandler : AuthorizationHandler<RolesAuthorizationRequirement>
      {
        private readonly IUserStore<CustomUser> _userStore;        
    
        public PermissionHandler(IUserStore<CustomeUser> userStore)
        {
          _userStore = userStore;         
        }
    
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement)
        {
          if(context == null || context.User == null)
            return;
    
          var userId = context.User.FindFirst(c => string.CompareOrdinal(c.Type, ClaimTypes.NameIdentifier) == 0);//according to msdn this method returns null if not found
    
          if(userId == null)
            return;
    
          // for simplicity, I use only one role at a time in the attribute
          //but you can use multiple values
          var permissions = requirement.AllowedRoles.ToList();
    
          var hasPermissions = //here is your logic to check the database for the actual permissions for this user.
                               // hasPermissions is just a boolean which is the result of your logic....
    
          if(hasPermissions)
            context.Succeed(requirement);//the user met your custom criteria
          else
            context.Fail();//the user lacks permissions.
        }
      }
    }
    

    Now inject the PermissionHandler in your startup.cs file like this:

        public void ConfigureServices(IServiceCollection services)
        {
              // Custom Identity Services
              ........
    
              // custom role checks, to check the roles in DB 
              services.AddScoped<IAuthorizationHandler, PermissionHandler>();
    
    //the rest of your injection logic omitted for brevity.......
        }
    

    Now use it in your actions like this:

    [Authorize(Roles = PermissionTypes.UserCreate)]
    public IActionResult Privacy()
    {
       return View();
    }
    

    Notice I did not create a custom attribute... Like I said there is many ways to do this. I prefer this way because is less code and there is no hard-coded policies or claims or any other complexities and you can make it 100% data driven.

    This is a complex subject so there might be extra tweaks necessary for it work.

    Also I use ASP.NET Core 2.2 which might be different than 3.0.

    But it should give you a way to do permission based Authorization.