ASP.NET Core – Accessing Custom AuthorizeAttribute Property in AuthorizeHandler

asp.net-coreasp.net-mvcauthenticationauthorizationc++

As I am working on Asp.Net core Authorization part, I needed a new property in AuthorizeAttribute which I want to utilize as a extra permission value. So, I have extended the AuthorizeAttribute in my own custom Authorize attribute. See below:

public class RoleAuthorizeAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute
    {
        public string Permission { get; private set; }

        public RoleAuthorizeAttribute(string policy, string permission) : base(policy)
        {
            this.Permission = permission;
        }
    }

Then, I've created an AuthorizationHandler to check for the requirement as below:

public class RolePermissionAccessRequirement : AuthorizationHandler<RolePermissionDb>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolePermissionDb requirement)
        {
            // check here..
            context.Succeed(requirement);

            return Task.FromResult(0);
        }
    }

All respective service collection mapping I have already done, just omitted here.

Now, I want my attribute to use like this on controller action level:

[RoleAuthorize("DefaultPolicy", "CustomPermission")]
public IActionResult List()
{
}

Would anybody suggest me how would I access the permission property value given on the top of Action method in the handler RolePermissionAccessRequirement ??

I want to perform some sort of access rule based on custom permission value given in the Authorize attribute on top of Action method.

Thanks in advance!

Best Answer

To parametrize a custom Authorize attribute, create an authorization filter implementing IAsyncAuthorizationFilter. Then wrap the filter in a TypeFilterAttribute-derived attribute. This attribute can accept parameters and pass it to the authorization filter's constructor.

Usage example:

[AuthorizePermission(Permission.Foo, Permission.Bar)]
public IActionResult Index()
{
    return View();
}

Implementation:

public class AuthorizePermissionAttribute : TypeFilterAttribute
{
    public AuthorizePermissionAttribute(params Permission[] permissions)
        : base(typeof(PermissionFilter))
    {
        Arguments = new[] { new PermissionRequirement(permissions) };
        Order = Int32.MinValue;
    }
}    

public class PermissionFilter : Attribute, IAsyncAuthorizationFilter
{
    private readonly IAuthorizationService _authService;
    private readonly PermissionRequirement _requirement;

    public PermissionFilter(
        IAuthorizationService authService, 
        PermissionRequirement requirement)
    {
        //you can inject dependencies via DI            
        _authService = authService;

        //the requirement contains permissions you set in attribute above
        //for example: Permission.Foo, Permission.Bar
        _requirement = requirement;
    }

    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        bool ok = await _authService.AuthorizeAsync(
            context.HttpContext.User, null, _requirement);

        if (!ok) context.Result = new ChallengeResult();
    }
} 

In addition, register a PermissionHandler in DI to handle PermissionRequirement with permission list:

public class PermissionHandler : AuthorizationHandler<PermissionRequirement>

Look at this this GitHub project for a complete example.

Related Question