Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding controller AuthorizeAttribute for just one action

Tags:

asp.net-mvc

I have a controller decorated with an AuthorizeAttribute. The controller contains several actions that all require authentication apart from one action that requires some custom authentication provided by CustomAuthorizeAttribute.

My question is once I've added [Authorize] at the controller level can I override it (or remove it) with [CustomAuthorize] on just one action? Or do I have to remove [Authorize] from the controller level and add it individually to every other action?

I'm asking purely for convenience because I'm lazy and don't want to decorate every action with the AuthorizeAttribute.

[Authorize]
public class MyController : Controller {

  //requires authentication
  public ViewResult Admin() {
    return View();
  }

  //... a lot more actions requiring authentication

  //requires custom authentication
  [CustomAuthorize]  //never invoked as already failed at controller level
  public ViewResult Home() {
    return View();
  }

}
like image 350
David Glenn Avatar asked Jan 15 '10 11:01

David Glenn


People also ask

How do I override an authorized attribute in .NET core?

Right-click on the solution and add a new class. Enter the class name and click on Add. Next Inherite Attribute, IAuthorizationFilter to CustomAuthorization class which has overridden the OnAuthorization method. The OnAuthorization Method has the AuthorizationFilterContext parameter.

Which attribute is used to override required authentication?

If a user is not authenticated, or doesn't have the required user name and role, then the Authorize attribute prevents access to the method and redirects the user to the login URL.


4 Answers

In MVC 5 you can override the authorization for any action using the new attribute OverrideAuthorization. Basically, you add it to an action that has a different authorization configuration than the one defined in the controller.

You do it like this:

[OverrideAuthorization]
[Authorize(Roles = "Employee")]
public ActionResult List() { ... }

More information at http://www.c-sharpcorner.com/UploadFile/ff2f08/filter-overrides-in-Asp-Net-mvc-5/

In ASP.NET Core 2.1 there's no OverrideAuthorization attribute and the only thing you can do is make an action anonymous, even if the controller is not. More information at https://docs.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-2.1

One option is to do it this way:

[Authorize(Roles = "Admin,Employee")] // admin or employee
public class XController : Controller 
{
    [Authorize(Roles = "Admin")] // only admin
    public ActionResult ActionX() { ... }

    [AllowAnonymous] // anyone
    public ActionResult ActionX() { ... }
}
like image 135
Francisco Goldenstein Avatar answered Oct 05 '22 03:10

Francisco Goldenstein


You can change the Order in which the attributes run (using the Order property), but I believe that in this case they will still both run unless one generates a result with immediate effect. The key is to have the least restrictive attribute applied at the highest level (class) and get more restrictive for the methods. If you wanted the Home action to be publicly available, for instance, you would need to remove the Authorize attribute from the class, and apply it to each of the other methods.

If the action has the same level of permissiveness, but has a different result, changing the order may be sufficient. For example, you would normally redirect to the Logon action, but for Home you want to redirect to the About action. In this, case give the class attribute Order=2 and the Home action attribute Order=1.

like image 33
tvanfosson Avatar answered Oct 05 '22 04:10

tvanfosson


After way too much time, I came up with a solution. You need to decorate your controller with a custom AuthorizeAttribute.

public class OverridableAuthorize : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var action = filterContext.ActionDescriptor;
        if(action.IsDefined(typeof(IgnoreAuthorization), true)) return;

        var controller = action.ControllerDescriptor;
        if(controller.IsDefined(typeof(IgnoreAuthorization), true)) return;

        base.OnAuthorization(filterContext);
    }
}

Which can be paired with AllowAnonymous on an Action

[AllowAnonymous]
like image 38
bmavity Avatar answered Oct 05 '22 05:10

bmavity


All you need to override the [Authorize] from the controller, for a specific action is to add

[AllowAnonymous] 

to the action you want to not be authorized (then add your custom attribute as required).

See the comments / intellisense :

Represents an attribute that marks controllers and actions to skip the System.Web.Mvc.AuthorizeAttribute during authorization.

Full Example

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Website
{
    public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            if (true)//Perform own authorization logic
                return; //simply return if request is authorized

            context.Result = new UnauthorizedResult();
            return; //this is not authorized
        }
    }

    [Authorize]
    public class WebsiteController : Controller
    {
        [HttpGet]
        [AllowAnonymous]//When this is added our Custom Attribute is hit, without it our attribute is not used as request already gets 401 from controller's Authorize
        [CustomAuthorize]
        public IActionResult Index()
        {
            return View(new ViewModel());
        }
}

Note

This approach will not work if you want to use the standard [Authorize] attribute on your action, with a custom policy e.g.

[Authorize]
public class WebsiteController : Controller
{
    [HttpGet]
    [AllowAnonymous]
    [Authorize("CustomPolicyName")] //Will not be run
    public IActionResult Index()
    {
        return View(new ViewModel());
    }
}


services.AddAuthorization(options =>
{
    options.AddPolicy("BadgeEntry", policy =>
    policy.RequireAssertion(context =>
        false //Custom logic here
    ));
});

...but if like the OP you want a Custom Attribute then you are good to go with my solution.

like image 37
MemeDeveloper Avatar answered Oct 05 '22 04:10

MemeDeveloper