ASP.NET Core : Overriding controller AuthorizeAttribute for just one action

Standard

In Asp.Net Core, creating custom authorize attributes is over. You’d better deal with policies now. If you want to override the controller AuthorizeAttribute for just one action with a stronger policy, nothing simpler :

[Authorize]
public class AuthorizedController : Controller
{
    [HttpGet("do-authorized-stuff/{id}")]
    public IActionResult Whatever(int id)
    { }

    [Authorize(Consts.AdminOnly)]
    [HttpGet("do-admin-stuff/{id}")]
    public IActionResult WhateverAdminStuff(int id)
    {
    }

}

But what if you want to override it with a weaker policy ? For example you want to override the admin policy just for one action which would be available to any authenticated user like so :

[Authorize(Consts.AdminOnly)]
public class AuthorizedController : Controller
{

    [Authorize] //admin policy is still applied :o(
    [HttpGet("do-authorized-stuff/{id}")]
    public IActionResult Whatever(int id)
    { }

    [HttpGet("do-admin-stuff/{id}")] //admin policy is applied
    public IActionResult WhateverAdminStuff(int id)
    { }

This won’t work as policies are accumulated, in other words it’s an AND not an OR.
Still, Asp.Net Core has a lot of possibilities and you could simply do in Startup.cs file :

services.AddAuthorization(options =>
{
    var simplyAuthenticated = new AuthorizationPolicy(new DenyAnonymousAuthorizationRequirementOverrideOthers().Yield(), new List<string>());
    options.AddPolicy(name: PolicyConsts.AuthenticatedOnlyOverridePolicyName, policy: simplyAuthenticated);
    options.AddPolicy(name: PolicyConsts.Admin, configurePolicy: policy => policy.RequireAssertion(e =>
    {
        if (e.Resource is AuthorizationFilterContext afc)
        {
            var noPolicy = afc.Filters.OfType<AuthorizeFilter>().Any(p => p.Policy.Requirements.Count == 1 && p.Policy.Requirements.Single() is DenyAnonymousAuthorizationRequirementOverrideOthers);
            if (noPolicy)
                return true;
        }
        return e.User.IsInRole(Consts.Admin);
    }));

}

and here’s the custom DenyAnonymousAuthorizationRequirementOverrideOthers :


namespace Whatever
{
    using Microsoft.AspNetCore.Authorization.Infrastructure;

    public class DenyAnonymousAuthorizationRequirementOverrideOthers : DenyAnonymousAuthorizationRequirement
    { }
}

 

and here’s the controller :


[Authorize(PolicyConsts.AdminOnly)]
public class AuthorizedController : Controller
{

    [Authorize(PolicyConsts.AuthenticatedOnlyOverride)]
    [HttpGet("do-authorized-stuff/{id}")]//admin policy is bypassed thanks to our startup.cs code
    public IActionResult Whatever(int id)
    { }

    [HttpGet("do-admin-stuff/{id}")] //admin policy is applied coming from controller
    public IActionResult WhateverAdminStuff(int id)
    { }
}

And you can add any custom policy you want and make it override others in the RequireAssertion Func of other policies.

Hope it helps, see ya !

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s