Question

Using an MVC4 application that uses .NET 4.5 and IIS 8. Is there a way to return a custom unauthorised page AND a 401 result, when a permission check fails?

I am also using Forms authentication and want a redirect to the login page when a user isn't logged in, but a redirect to an unauthorised page when a user does not have the valid permission (i.e. Admin).

I also have to be careful of redirecting and then achieving a 200/301 result on successful redirect.

I need to return 401 status code and stay on the current page, whilst also returning the custom unauthorized page.

I have an Error controller that returns an unauthorized view:

[ActionName("unauthorised")]
    public ActionResult Http401() {
      Response.StatusCode = 401;
      Response.StatusDescription = "Unauthorised";
      Response.SuppressFormsAuthenticationRedirect = true;
      return View("unauthorised");
    }

And I have some methods that I am using to try and invoke a 401 if a check for a permission fails:

/// <summary>Method to invoke when user attempts action without relevant permission</summary>
    /// <param name="httpContext">the httpcontext</param>
    /// <returns>empty result</returns>
    public ActionResult InvokeHttp401(HttpContextBase httpContext) {
      Transfer("/error/unauthorised");
      return new EmptyResult();
    }

    private void Transfer(string url) {
      var uriBuilder = new UriBuilder(Request.Url.Scheme, Request.Url.Host,    Request.Url.Port, Request.ApplicationPath); // Create URI builder
      uriBuilder.Path += url; // Add destination URI
      string path = Server.UrlDecode(uriBuilder.Uri.PathAndQuery); // Because UriBuilder escapes URI decode before passing as an argument
      this.HttpContext.RewritePath(path, false); // Rewrite path
      HttpContext.Server.TransferRequest(path, true);
    }

Note: I am using Server.TransferRequest so that I can try and return the 401 result on the current url, without causing any redirect. I am checking permission in a regular ActionResult as follows:

[Authorize]
public ActionResult MyMethod() {
  if (!UserHasPermission(Admin))
    return InvokeHttp401(HttpContext);
    ......

Where the authorize tag will redirect to login if a user is not logged in, other a permissions check is made.

With the above setup, I am able to return a 401 result and stay on the current url, but the IIS 401 error is displayed instead of my current page.

I have also tried to set the following in my web.config file:

<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace">
      .....
      <error statusCode="401" responseMode="ExecuteURL" path="/Error/unauthorised" />
    </httpErrors>

But this makes no difference. I am able to get the custom unauthorised page to appear if I do not set the status code in my Http401 ActionResult, but it is just returned with a 200 result then.

I have looked at the posts on this question (particularly Timothy's answer which extended the authorize tags), but was unable to get any of this working without similar errors as already described.

Can anyone help here?

Was it helpful?

Solution

Figured it out finally -

Using a combination of the Custom authorize tags as suggested by Timothy in the linked question, and this blog post, I am able to temporarily set the status code to 418, and then change it back to 401 in Global.End_Request.

The one last thing I had to do was add 418 to my web.config and use the PassThrough mode:

<httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough">
      <remove statusCode="404" />
      <error statusCode="404" responseMode="ExecuteURL" path="/Error/not-found" />
      <remove statusCode="500" />
      <error statusCode="500" responseMode="ExecuteURL" path="/Error/error" />
      <remove statusCode="401" />
      <error statusCode="401" responseMode="ExecuteURL" path="/Error/unauthorised" />
    </httpErrors>

And I get a 401 result on the same url, as well as my custom view.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top