0

In my base controller I have a OnAuthorization event handler and in my Controllers I have handlers that sometimes have [Authorize] and sometimes not. These controllers inherit from base controller.

What I would expect is for OnAuthorization to be triggered when authorization is actually needed (method or controller having [Authorize].

This is not the case and the OnAuthorization is triggered for every method.

Is there a way to prevent this from happening or to detect the method/class not having the [Authorize] attribute?

Currently I want to authorize only 5 or the 25 or so handlers so it would be great to have a single method handling this without having to change all the controllers.

HMR
  • 37,593
  • 24
  • 91
  • 160

2 Answers2

4

See Filtering in MVC.

The reason why the MVC controller OnAuthorization method functions is because the Controller class implements IAuthorizationFilter and MVC uses the ControllerInstanceFilterProvider to register all controllers as global filters.

Global authorization filters run on every request. It is up to you to determine whether they qualify for the authorization check or not. If you want to have the authorization run when the AuthorizeAttribute doesn't exist, you need to add a condition to check whether the AuthorizeAttribute exists, and skip the custom authorization check if it does.

protected override void OnAuthorization(AuthorizationContext filterContext)
{
    bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AuthorizeAttribute), inherit: true)
        || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AuthorizeAttribute), inherit: true);

    if (skipAuthorization)
    {
        return;
    }

    // Do your authorization here...

    base.OnAuthorization(filterContext);
}

The reason why this doesn't happen by default is that AuthorizeAttribute is a different instance of IAuthorizationFilter than the controller instance. AuthorizeAttribute is both an IAuthorizationFitler and a FilterAttribute. When you place it on an action method, it registers with the framework in such a way that it only executes on that action method. The controller IAuthorizationFilter on the other hand is always registered globally, so it runs on every request. These 2 authorization filters are completely unaware of each other.

On a side note, you should never use a base controller class in MVC. For cross-cutting concerns, you can use globally registered filters. This is how the OnAuthorization method works in a controller - it is a global filter. However, you can separate the cross-cutting code into filter classes, which are more SOLID and loosely coupled from your controllers. See this answer for further explanation and an example.

Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • Thank you for your answer, I was unable to find a reference to confirm that OnAuthorization is triggered every time; no matter what the annotation of the class or method. The msdn documentation states "Called when a process requests authorization." but how is a method that does not have `[Authorize]` in a class that does not have `[Authorize]` require authentication? – HMR Jun 30 '16 at 03:10
  • The `AuthorizeAttribute` is a combination of 2 different things - an `IAuthorizationFilter` and an `Attribute`. The filter is what runs. The attribute is a marker that acts as a condition *when* to run it (and optionally, what users and roles should have access). If you make a custom filter, you can put any conditions in that you want. `OnAuthorization` of the controller functions, because the MVC Controller class implements `IAuthorizationFilter`. See [Filtering in MVC](https://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx). – NightOwl888 Jun 30 '16 at 07:45
  • Thank you again for your reply. I actually started using a custom attribute. The existing code used OnAuthorization method on a base controller and intuitively I thought this was a handler for when authorization is needed. But if it's an event handler it gets triggered like an sjw at a Trump rally. Kind of like a html button onClick gets triggered on mouse move. Guess they'd better rename that function to onWheneverIWantTo. – HMR Jun 30 '16 at 15:06
  • Actually, no it makes sense if you look at the definition of [IAuthorizationFilter.OnAuthorization](https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4e40cdef9c8a8226685f95ef03b746bc8322aa92/src/System.Web.Mvc/IAuthorizationFilter.cs). All of the filters run on every request. You have to check authorization on every request because HTTP is stateless. That is why they are called filters - the code you place within them sets up the filtering conditions (usually from the current request) when they are supposed to *do* something before or after the action method runs. – NightOwl888 Jun 30 '16 at 17:13
  • It would benefit readers if you mentioned that your method needs to be placed in a class that inherits from AuthorizeAttribute. public sealed class AuthorizeWithMessage : AuthorizeAttribute { } – Spencer Sullivan Nov 19 '19 at 22:53
  • @SpencerSullivan - Actually, not it doesn't *need* to. It only needs to implement `IAuthorizationFilter` and be registered (either globally or via an Attribute), which was my point. Filters and Attributes are two separate things, and it is unfortunate that Microsoft decided to (confusingly) put them into the same class, as in the case of `AuthorizeAttribute`. Ideally, *behavior* would be implemented in the filter only, and the Attribute would only consist of metadata (that doesn't need its own tests). See: [Passive Attributes](https://blog.ploeh.dk/2014/06/13/passive-attributes/). – NightOwl888 Nov 20 '19 at 09:03
0

You can exclude authorization for specific action/method by using [AllowAnonymous]

example:

[AllowAnonymous] public ActionResult MyAction() { }

  • Thank you for your reply, I was hoping to implement only the functions that require Authentication so there is no need to change the entire code base. Why would OnAuthorization even be triggered for classes and methods that do not require authorization (no explicit authorization is added) – HMR Jun 30 '16 at 02:36