Home » General » Asp.Net MVC HTTP Error 401.0 – Unauthorized

Asp.Net MVC HTTP Error 401.0 – Unauthorized

HTTP Error 401.0 – Unauthorized
You do not have permission to view this directory or page.

When an unauthorized action is trigged, the HttpUnauthorizedResult result called. It produces a 401 error. By default, a 401 error can be handled only by IIS and not directly like a 404 error with Asp. This would be easy if we could set a 401 error page in the web.config, but it’s not the case.

namespace System.Web.Mvc { 

    public class HttpUnauthorizedResult : HttpStatusCodeResult {

        // HTTP 401 is the status code for unauthorized access. Other code might 
        // intercept this and perform some special logic. For example, the
        // FormsAuthenticationModule looks for 401 responses and instead redirects 
        // the user to the login page. 
        private const int UnauthorizedCode = 401;
 
        public HttpUnauthorizedResult()
            : this(null) {
        }
 
        public HttpUnauthorizedResult(string statusDescription)
            : base(UnauthorizedCode, statusDescription) { 
        } 
    }
} 
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

What can be done is to transform the 401 error code into a standard code, like the 200 code. This response header code indicate that the webpage has been found. This will let us counter the 401 behaviors and redirect to a specific actions.

public abstract class HttpUnauthorizedWithRedirectToResultBase : HttpUnauthorizedResult
{
	protected ActionResult _result;

	public override void ExecuteResult(System.Web.Mvc.ControllerContext context)
	{
		if (context == null)
			throw new ArgumentNullException("context");

		if (context.HttpContext.Request.IsAuthenticated)
		{
			context.HttpContext.Response.StatusCode = 200;
			InitializeResult(context);
			_result.ExecuteResult(context);
		}
		else
			base.ExecuteResult(context);
	}

	protected abstract void InitializeResult(ControllerContext context);
}

We will create a new http unauthorized that will set area and view name. This will let us set the default page if we do not want to specify every time which action to use. If nothing will be defined, we will be able to simply put in the shared folder a view with the error.

public class HttpUnauthorizedWithRedirectToViewResult : HttpUnauthorizedWithRedirectToResultBase
{
	private readonly string _area;
	private readonly string _viewName;
	
	public HttpUnauthorizedWithRedirectToViewResult(string viewName, string area)
	{
		_viewName = viewName;
		_area = area;
	}

	protected override void InitializeResult(ControllerContext context)
	{
		if (!string.IsNullOrWhiteSpace(_area))
		{
			context.RequestContext.RouteData.DataTokens["area"] = _area;
		}
		_result = new ViewResult{ViewName = _viewName};
	}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AuthorizeWith401Support : AuthorizeAttribute
{
	private const string VIEW_NAME = "NoAccess401";
	private string _actionName;

	public string ActionName
	{
		get	{return string.IsNullOrWhiteSpace(_actionName)? VIEW_NAME : _actionName;}		
		set { _actionName = value; }
	}

	public string Controller { get; set; }
	public string Area { get; set; }

	protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
	{
		if (filterContext.IsChildAction)
		{
			base.HandleUnauthorizedRequest(filterContext);
		}
		else
		{
			if (string.IsNullOrWhiteSpace(ActionName))
                throw new ArgumentException("You must set an ActionName");

            if (string.IsNullOrWhiteSpace(controller))
                filterContext.Result =  new HttpUnauthorizedWithRedirectToViewResult(ActionName, area);
 		}
	}
}

Now, you need to use it as you would use the AuthorizeAttribute. It’s the same behavior because the AuthorizeWith401Support inherit from AuthorizeAttribute. However, you have the additionnal ‘ActionName’, ‘Area’ and ‘Controller’ property that let you specify the error action. Of course, if nothing is specifie, the action ‘NoAccess401’ will be used.

[AuthorizeWith401Support(Roles = "admin")]
public ActionResult Index()
{
	//...
}

If you like my article, think to buy my annual book, professionally edited by a proofreader. directly from me or on Amazon. I also wrote a TypeScript book called Holistic TypeScript

One Response so far.

  1. Neil says:

    Thanks this was a really helpful post.

    I didn’t have to do quite as much though so opted for the below and binding a filter to the attribute using Ninject.


    public class CustomAuthoriseAttribute : AuthorizeAttribute
    {
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
    filterContext.Result = new RedirectResult("/Error/NotAuthorised");
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.