84

I have an MVC controller base class on which I applied the Authorize attribute since I want almost all of the controllers (and their actions along) to be authorized.

However I need to have a controller and an action of another controller unauthorized. I wanted to be able to decorate them with the [Authorize(false)] or something but this is not available.

Any ideas?

pb2q
  • 58,613
  • 19
  • 146
  • 147
Andrei Rînea
  • 20,288
  • 17
  • 117
  • 166

4 Answers4

102

Edit: Since ASP.NET MVC 4 the best approach is simply to use the built-in AllowAnonymous attribute.

The answer below refers to earlier versions of ASP.NET MVC

You could create a custom authorisation attribute inheriting from the standard AuthorizeAttribute with an optional bool parameter to specify whether authorisation is required or not.

public class OptionalAuthorizeAttribute : AuthorizeAttribute
{
    private readonly bool _authorize;

    public OptionalAuthorizeAttribute()
    {
        _authorize = true;
    }

    public OptionalAuthorizeAttribute(bool authorize)
    {
        _authorize = authorize;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if(!_authorize)
            return true;

                    return base.AuthorizeCore(httpContext);
    }
}

Then you can decorate your base controller with that attribute:

[OptionalAuthorize]
public class ControllerBase : Controller
{
}

and for any controllers you don't want authorisation simply use the override with a 'false' - e.g.

[OptionalAuthorize(false)]
public class TestController : ControllerBase
{
    public ActionResult Index()
    {
        return View();
    }
}
Steve Willcock
  • 26,111
  • 4
  • 43
  • 40
  • I have thought of this but I was hoping for a simpler solution. However if "they" didn't provide one then your solution is the best one. – Andrei Rînea Apr 14 '09 at 12:07
  • 2
    It is better to use `[AllowAnonymous]` attribute. – Jaider Sep 25 '15 at 20:32
  • Wait... so the controller honors only the top-level class's attribute of a particular type? – Triynko Nov 04 '15 at 16:21
  • Do you know why this is actually BETTER to use AllowAnonymous? Because you have finer control. In my case, I'm looking to disable Authorization endpoints only for certain environments, you don't need it for localhost for example. This provides a more elegant solution than what I was gonna do in my startup.cs. We're talking 11 years later here. – sksallaj Mar 23 '20 at 20:21
78

It seems ASP.NET MVC 4 'fixed' this by adding an AllowAnonymous attribute.

David Hayden wrote about this :

[Authorize]
public class AccountController : Controller
{
    [AllowAnonymous]
    public ActionResult Login()
    {
        // ...
    }

    // ...
}
Andrei Rînea
  • 20,288
  • 17
  • 117
  • 166
15

My personal take on this would be to split the controller. Just create another controller For the actions you don't need authentication.

Or you could have :

  • BaseController
    doesn't require authentication - here you have all your "base stuff" :).

  • BaseAuthController : BaseController
    all actions here require authentication.

That way you can have authentication when you want , just by deriving from a specific class.

splattne
  • 102,760
  • 52
  • 202
  • 249
sirrocco
  • 7,975
  • 4
  • 59
  • 81
6

If you just want one action to be unauthorized on an otherwise authorized controller you can do something like this:

public class RequiresAuthorizationAttribute : ActionFilterAttribute
{
    private readonly bool _authorize;

    public RequiresAuthorizationAttribute()
    {
        _authorize = true;
    }

    public RequiresAuthorizationAttribute(bool authorize)
    {
        _authorize = authorize;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var overridingAttributes = filterContext.ActionDescriptor.GetCustomAttributes(typeof (RequiresAuthorizationAttribute), false);

        if (overridingAttributes.Length > 0 && overridingAttributes[0] as RequiresAuthorizationAttribute != null && !((RequiresAuthorizationAttribute)overridingAttributes[0])._authorize)
            return;

        if (_authorize)
        {
            //redirect if not authenticated
            if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                //use the current url for the redirect
                var redirectOnSuccess = filterContext.HttpContext.Request.Url.AbsolutePath;

                //send them off to the login page
                //var redirectUrl = string.Format("?RedirectUrl={0}", redirectOnSuccess);
                var loginUrl = LinkBuilder.BuildUrlFromExpression<HomeController>(filterContext.RequestContext, RouteTable.Routes,
                                                                                  x => x.Login(redirectOnSuccess));
                filterContext.HttpContext.Response.Redirect(loginUrl, true);
            }
        }
    }
}
pondermatic
  • 6,453
  • 10
  • 48
  • 63