Can you enable [Authorize] for controller but disable it for a single action?

16,870

Solution 1

I don't think you can do this with the standard Authorize attribute, but you could derive your own attribute from AuthorizeAttribute that takes a list of actions to allow and allows access to just those actions. You can look at the source for the AuthorizeAttribute at www.codeplex.com for ideas on how to do this. If you did, it might look like:

[AdminAuthorize (Roles = "Administrator", Exempt = "Login, Logout") ]
public class AdminController : Controller
{
    public ActionResult Login()
    {
        return View();
    }

    public ActionResult Login()
    {
        return View();
    }

    ... other, restricted actions ...
}

EDIT: FYI, I eventually ran across a need to do something similar on my own and I went a different direction. I created a default authorization filter provider and apply a global authorize filter. The authorization filter provider uses reflection to check if an action or controller has a specific authorize attribute applied and, if so, defers to it. Otherwise, it applies a default authorization filter. This is coupled with a PublicAttribute derived from AuthorizeAttribute that allows public access. Now, I get default secured access, but can grant public access via [Public] applied to an action or controller. More specific authorization can also be applied as necessary. See my blog at http://farm-fresh-code.blogspot.com/2011/04/default-authorization-filter-provider.html

Solution 2

You can decorate your controller with [Authorize] and then you can just decorate the method that you want to exempt with [AllowAnonymous]

Solution 3

You could override the OnAuthorization method of the controller

    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        if ((string)(filterContext.RouteData.Values["action"]) == "Login")
        {
            filterContext.Cancel = true;
            filterContext.Result = Login();
        }
    }

This works but it is a hack.

Full class code used for testing:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;

namespace MvcApplication2.Controllers
{
[HandleError]
[Authorize]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewData["Title"] = "Home Page";
        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }


    public ActionResult About()
    {
        ViewData["Title"] = "About Page";

        return View();
    }


    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        if ((string)(filterContext.RouteData.Values["action"]) == "Index")
        {
            filterContext.Cancel = true;
            filterContext.Result = Index();
        }
    }
}
}

Solution 4

May be it's not actual, but I wrote my custom attribute:

public class SelectableAuthorizeAttribute : AuthorizeAttribute
{
    public SelectableAuthorizeAttribute(params Type[] typesToExclude)
    {
        _typesToExlude = typesToExclude;
    }

    private readonly Type[] _typesToExlude;

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        bool skipAuthorization = _typesToExlude.Any(type => filterContext.ActionDescriptor.ControllerDescriptor.ControllerType == type);

        if (!skipAuthorization)
        {
            base.OnAuthorization(filterContext);
        }
    }
}

And then registered it in my global filetrs:

filters.Add(new SelectableAuthorizeAttribute(typeof(MyController)));

Hope that it will be useful for someone

Share:
16,870

Related videos on Youtube

Todd Smith
Author by

Todd Smith

Updated on April 16, 2022

Comments

  • Todd Smith
    Todd Smith about 2 years

    I would like to use [Authorize] for every action in my admin controller except the Login action.

    [Authorize (Roles = "Administrator")]
    public class AdminController : Controller
    {
        // what can I place here to disable authorize?
        public ActionResult Login()
        {
            return View();
        }
    }
    
  • Todd Smith
    Todd Smith over 15 years
    Looks like you're correct according to codeplex.com/aspnet/SourceControl/changeset/view/17272 I'm guessing the attribute gets applied at the controller level and never gives the action an attempt to evaluate it.
  • Todd Smith
    Todd Smith over 15 years
    Hmm apparently you can't link directly to the source file on codeplex.
  • Todd Smith
    Todd Smith over 15 years
    Does this get called before or after the [Authorize] attribute?
  • Todd Smith
    Todd Smith over 15 years
    It looks like the [Authoirze] attribute is evaluated first and never gets to the OnAuthoirization method.
  • MrJavaGuy
    MrJavaGuy over 15 years
    Gets called every time for me.
  • MrJavaGuy
    MrJavaGuy over 15 years
    Looking again at your question, you should be able to use the line fliterContext.Result = View("Login"); as well
  • Todd Smith
    Todd Smith over 15 years
    OnAuthorization is getting called except when I have a [Authorize] attribute on the controller itself.
  • MrJavaGuy
    MrJavaGuy over 15 years
    Are you using the beta of the MVC or one of the previews? I have the [Authorize] attribute on the controller as well.
  • Todd Smith
    Todd Smith over 15 years
    I'm using the beta. Perhaps the difference is the roles [Authorize (Roles = "Administrator")]
  • MrJavaGuy
    MrJavaGuy over 15 years
    glad to know I am not crazy :)
  • Todd Smith
    Todd Smith over 15 years
    There is one catch with this approach, if you have two versions of your login, one for get and one for post and your methods are decorated with [AcceptVerbs(HttpVerbs.Get)] and [AcceptVerbs(HttpVerbs.Post)] then the OnAuthorization method gets a bit more complicated.
  • MrJavaGuy
    MrJavaGuy over 15 years
    only a little, use filterContext.HttpContext.Request.HttpMethod for the verb
  • mipe34
    mipe34 about 11 years
    Note, that Cancel property of AuthorizationContext has been removed since RC1. stackoverflow.com/questions/505653/…
  • Martin Hansen Lennox
    Martin Hansen Lennox over 10 years
    Isn't this the correct answer? Or perhaps this was not the case in 2008 when the question was asked...
  • Ben Pretorius
    Ben Pretorius almost 3 years
    Which framework are you having trouble with?