Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.net MVC4: Authorize on both controller and action

If I have the Authorize attribute on both the controller and the action, which one will take the effect? Or will both take effect?

like image 434
frank Avatar asked May 23 '13 09:05

frank


People also ask

Can we have two action methods with same name in MVC?

While ASP.NET MVC will allow you to have two actions with the same name, . NET won't allow you to have two methods with the same signature - i.e. the same name and parameters. You will need to name the methods differently use the ActionName attribute to tell ASP.NET MVC that they're actually the same action.

Which attribute will ensure that all user can access a specific controller action?

In its most basic form, applying the [Authorize] attribute to a controller, action, or Razor Page, limits access to that component to authenticated users.

Which of the bellow attribute allows you to provide anonymous access to a controller or action?

However, we need to allow anonymous access to the login and register controller actions so we decorate them with the AllowAnonymous Attribute which negates the Authorize Attribute and allows anonymous access.

How do you enable access to certain controllers actions for anonymous users in ASP.NET MVC 5?

In ASP.NET MVC, by default, all the action methods are accessible to both anonymous and authenticated users. But, if you want the action methods to be available only for authenticated and authorized users, then you need to use the AuthorizationFilter in MVC.


2 Answers

You asked:

If I have Authorize attribute on both controller and action, which one will take the effect? Both?

To answer this simply: Both. The effect is to AND the two restrictions together. I'll explain why below ...

Details

So, there are a few reasons you could be asking this.

  1. You want to know how to enforce an additional constraint on an Action compared to a method. e.g.
    • At controller level, enforce users in the role "user"
    • At an action level, additionally enforce users in the role "admin"
  2. You want to replace the controller constraint at the action level
  3. You want to remove the controller constraint at the action level and make the method available to anonymous users

You didn't specify your MVC version, so I will assume the latest as of today (MVC 4.5). However, that won't change of the answer much even if you were using MVC 3.

[Anonymous] overrides controller [Authorize] (case 3)

Case 3. I don't need to cover (the use of [AllowAnonymous]) as it has been answered all over SO and all over the web already. Suffice to say: if you specify [AllowAnonymous] on an action it will make that action public even if the controller has [Authorize] on it.

You can also make an entire website subject to authorisation by using a global filter, and use AllowAnonymous on the few actions or controllers you want to make public.

[Authorize] is additive (case 1)

Case 1 is easy. Take the following controller as an example:

[Authorize(Roles="user")] public class HomeController : Controller {     public ActionResult AllUsersIndex() {         return View();     }      [Authorize(Roles = "admin")]     public ActionResult AdminUsersIndex() {         return View();     } } 

By default [Authorize(Roles="user")] makes all Actions in the Controller available to accounts in the "user" role only. Therefore to access AllUsersIndex you must be in the "user" role. However to access AdminUsersIndex you must be both in the "user" and the "admin" role. For example:

  • UserName: Bob, Roles: user, cannot access AdminUsersIndex, but can access AllUsersIndex
  • UserName: Jane, Roles: admin, cannot access AdminUsersIndex or AllUsersIndex
  • UserName: Tim, Roles: user & admin, can access AdminUsersIndex and AllUsersIndex

This illustrates that the [Authorize] attribute is additive. This is also true of the Users property of the attribute, which can be combined with Roles to make it even more restrictive.

This behaviour is due to the way that controller and action attributes work. The attributes are chained together and applied in the order controller then action. If the first one refuses authorization, then control returns and the action's attribute is not called. If the first one passes authorization, then the second one is then checked as well. You can override this order by specifying Order (for example [Authorize(Roles = "user", Order = 2)]).

Overriding [Authorize] (case 2)

Case 2 is trickier. Recall from above that the [Authorize] attributes are examined in the order (Global then) Controller then Action. The first one to detect that the user is ineligible to be authorized wins, the others don't get called.

One way around this is to define two new attributes as below. The [OverrideAuthorize] does nothing other than defer to [Authorize]; its only purpose is to define a type that we can check for. The [DefaultAuthorize] allows us to check to see if the Action being called in the request is decorated with a [OverrideAuthorize]. If it is then we defer to the Action authorization check, otherwise we proceed with the Controller level check.

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

We can then use it like this:

[DefaultAuthorize(Roles="user")] public class HomeController : Controller {     // Available to accounts in the "user" role     public ActionResult AllUsersIndex() {         return View();     }     // Available only to accounts both in the "user" and "admin" role     [Authorize(Roles = "admin")]     public ActionResult AdminUsersIndex() {         return View();     }     // Available to accounts in the "superuser" role even if not in "user" role     [OverrideAuthorize(Roles = "superuser")]     public ActionResult SuperusersIndex() {         return View();     } } 

In the above example SuperusersIndex is available to an account that has the "superuser" role, even if it does not have the "user" role.

like image 71
Andy Brown Avatar answered Sep 22 '22 18:09

Andy Brown


I would like to add something to Overriding [Authorize] (case 2)

OverrideAuthorizeAttribute and DefaultAuthorizeAttribute works fine, but I discover that you can also use OverrideAuthorizationAttribute which overrides authorization filters defined at a higher level.

[Authorize(Roles="user")] public class HomeController : Controller {     // Available to accounts in the "user" role     public ActionResult AllUsersIndex() {         return View();     }     // Available only to accounts both in the "user" and "admin" role     [Authorize(Roles = "admin")]     public ActionResult AdminUsersIndex() {         return View();     }     // Available to accounts in the "superuser" role even if not in "user" role     [OverrideAuthorization()]     [Authorize(Roles = "superuser")]     public ActionResult SuperusersIndex() {         return View();     } } 
like image 31
Akodo_Shado Avatar answered Sep 22 '22 18:09

Akodo_Shado