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?

4 Answers 4

103

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();
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

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.
It is better to use [AllowAnonymous] attribute.
Wait... so the controller honors only the top-level class's attribute of a particular type?
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.
79

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()
    {
        // ...
    }

    // ...
}

Comments

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.

Comments

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);
            }
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.