ASP.NET MVC Authorization Filter - asp.net-mvc

If I register a global Authorize attribute in FilterConfig.cs so that each action method can only be accessible to authenticated users, and decorate some controllers with [Authorize(Role="Admin")] so that only admin users can access them, does authorization logic run twice on these controllers? What can I do to prevent that?

You can use an ASP.NET MVC "FilterProvider" provider. What this will do is help you to fetch all relevant filters from a specific controller and action.
So you can define your own provider and register that one instead of the default ones. this will give you full control over the asp.net filters and you can remove some filters based on your requirement.
Lets say we have following Controller.
[Authorize]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Whatever()
{
return View();
}
}
I think you are looking a way to do something as follows. concentrate on Index Action
[Authorize]
public class HomeController : Controller
{
[ExcludeFilter(typeof(AuthorizeAttribute))] // Excluding Authorize Important !
public ActionResult Index()
{
return View();
}
public ActionResult Admin() // will follow the declared authorize Attribute
{
return View();
}
}
If thats what you are Looking for then see this Article

Related

How do I skip the authorization for a specic user agent using asp.net MVC?

SO. I have an Action method in a controller as here below:
[Authorize]
public ActionResult ToDo()
{
//Do some stuff
return View();
}
I would like to allow facebook crawler access the content of the razor view for ToDo action method. How is that possible? Your thoughts.
create two action with same name but overload that for example
[Authorize]
[Route("Home/Index/{status:boolean}")]
Public ActionResult Index(bool status)
{
}
[Route("Home/Index/{id:int}")]
Public ActionResult Index(int id)
{
}
and you can create customeActionInvoker

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?
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.
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"
You want to replace the controller constraint at the action level
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.
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();
}
}
If use it on controller then, all methods of this controller will effected.
[Authorize]
public class SomeController(){
// all actions are effected
public ActionResult Action1
public ActionResult Action2
If you want to prevent for one of these actions, you can use something like this:
[Authorize]
public class SomeController(){
// all actions are effected
public ActionResult Action1
public ActionResult Action2
[AllowAnonymous]
public ActionResult Action3 // only this method is not effected...
I made an adaptation of this answer's second case for ASP.NET Core 2.1.
The difference with ASP.NET Core's AuthorizeAttribute is that you don't have to call AuthorizeAttribute.OnAuthorization base method to proceed to normal authorization. This means that even if you don't explicitly call the base method, the base AuthorizeAttribute could still short-circuit authorization by forbidding access.
What I did is that I created a DefaultAuthorizeAttribute that does not inherit from AuthorizeAttribute, but from Attribute instead. Since the DefaultAuthorizeAttribute does not inherit from AuthorizeAttribute, I had to recreate the authorization behavior.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class DefaultAuthorizeAttribute : Attribute, IAuthorizationFilter
{
private readonly AuthorizeFilter m_authorizeFilter;
public DefaultAuthorizeAttribute(params string[] authenticationSchemes)
{
var policyBuilder = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(authenticationSchemes)
.RequireAuthenticatedUser();
m_authorizeFilter = new AuthorizeFilter(policyBuilder.Build());
}
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
if (filterContext.ActionDescriptor is ControllerActionDescriptor controllerAction
&& controllerAction.MethodInfo.GetCustomAttributes(typeof(OverrideAuthorizeAttribute), true).Any())
{
return;
}
m_authorizeFilter.OnAuthorizationAsync(filterContext).Wait();
}
}
public class OverrideAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext) { }
}

Is it possible to disable authorization on one action in an MVC controller?

I have an authorization attribute on a controller, but I'd like to turn it off on one action.
I created my own authorization filter and added "Anonymous" into the Roles list. In my filter I then return true if Anonymous appears in the role list.
However, it doesn't seem to get past the login page as if the controller authorization is pre-empting anything else.
You can add [Authorize] To the controller class, and then add [AllowAnonymous] to the single action you don't want to be authorized. Example:
[Authorize]
public class AccountController : Controller
{
public ActionResult Profile()
{
return View();
}
[AllowAnonymous]
public ActionResult Login()
{
return View();
}
}
You can create your own version of the attribute.
There is a very similar question and there is a pretty good answer how to implement your own attribute that handles this situation.
Override Authorize Attribute in ASP.NET MVC
Btw. you could also create your controller that would have authorization by default.
Base
[Authorize]
public abstract class SecureControllerBase : Controller
{
}
Usage
public class MyController : SecureControllerBase
{
}
I just did a solution using Azure ACS as the federated Identity Provider and the accepted answer didn't work for me. For those who are struggling, my solution was to bypass the security altogether for the required controller/views.
Create a new Controller/Views for those actions which you need to bypass the authorization.
And in the web.config add the following ,
<location path="TheNameOfTheControllerYouWantToBypass">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
Simply add the attribute to the Actions you want to filter, and not on the controller class. By not decorating actions, they will not be filtered, provided the controller or one of its base controllers hasn't got the attribute.
Do not add AuthorizationAttribute on your action method where ever you do not required for example.
My custom attribute
public class AuthorizationFilterAttribute : AuthorizeAttribute
{
// Some code...
}
My controller
public class UserController : BaseController, IDisposable
{
[AuthorizationFilterAttribute]
public ActionResult UserList()
{
// Authorize attribute will call when this action is executed
}
public ActionResult AddUser()
{
// Authorize attribute will not call when this action is executed
}
}
I hope you got my point what I am trying to say you.
============================ Updated Answer ================================
Create one more attribute like below.
public sealed class AnonymousAttribute : Attribute { }
Please put below code on your OnAuthorization method.
public override void OnAuthorization(AuthorizationContext filterContext)
{
bool checkForAuthorization =
filterContext.ActionDescriptor.IsDefined(typeof(AnonymousAttribute), true) ||
filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AnonymousAttribute), true);
if (!skipAuthorization)
{
base.OnAuthorization(filterContext);
}
}

Exclude some actions from Authorize in ASP.net MVC

There is an authorize attribute on top of my contoller that means it contains all of my actions.
I want to exclude some actions from this attribute (these actions be available by anonymous users). Is it possible?
[Authorize]
public class TestController : Controller
{
public ActionResult Index()
{
...
}
...
//available by anonymous
public ActionResult Test()
{
...
}
}
You can take the approach outlined in this blog post of creating an AllowAnonymous attribute and placing this attribute on actions you wish to exclude:
http://blogs.msdn.com/b/rickandy/archive/2011/05/02/securing-your-asp-net-mvc-3-application.aspx
As of MVC 4, the AllowAnonymous attribute is stock and can be applied as needed.
Putting the [Authorize] attribute on the controller is basically a shortcut to putting it on every action, so your code is logically equivalent to
// No [Authorize] here
public class TestController : Controller
{
[Authorize]
public ActionResult Index()
{
// code here...
}
[Authorize]
public ActionResult Test()
{
// code here...
}
}
You can probably see where I'm going with this - remove the attribute from the controller, and put it on the specific actions that you want to be restricted:
// No [Authorize] here
public class TestController : Controller
{
[Authorize]
public ActionResult Index()
{
// code here...
}
// no [Authorize] here either, so anonymous users can access it...
public ActionResult Test()
{
// code here...
}
}
You might want to put the attribute on top of the restricted actions and leave the others (the ones in which you want to allow anonymous access) alone.
Also take it out of the top of the class.

Overriding controller AuthorizeAttribute for just one action

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();
}
}
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://learn.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() { ... }
}
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.
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]
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.
Override for all controllers when handling prototype and production environment.
So there is no need to remove the authorize of each controller.
app.UseEndpoints(endpoint =>
{
endpoint.MapControllers().WithMetadata(new AllowAnonymousAttribute());
});

Resources