Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom 401 error page for ASP.NET MVC application

I created an ASP.NET MVC application that uses integrated Windows Authentication. The following authorization logic was implemented:

  1. Try to get account credentials from active directory through NTLM.

    • If credentials are received and the Windows account has required permissions then perform requested action.
    • In otherwise go to clause 2.
  2. Display the Windows authentication dialog, so a user can provide another credentials: Authentication Dialog

    • If another credentials are received and the user has required permissions then perform requested action.
    • If another credentials are not received or the account permissions are less then it requires, then repeat clause 2.
    • If authentication dialog is cancelled then display 401 error message.

Then I have added custom error pages for the application, using VictorySaber's solution:

protected void Application_EndRequest()
{
    int status = Response.StatusCode;
    string actionName;
    if (status >= 400 && status < 500)
    {
        switch (status)
        {
            case 401:
                actionName = "Unauthorized";
                break;

            // Handle another client errors

            default:
                actionName = "BadRequest";
                break;
            }
        }
    else if (status >= 500 && status < 600)
    {
        switch (status)
        {
            case 501:
                actionName  = "NotImplemented";
                break;

            // Handle another server errors

            default:
                actionName  = "InternalServerError";
                break;
        }
    }
    if (!String.IsNullOrEmpty(actionName))
    {
        Response.Clear();
        var rd = new RouteData();
        rd.Values["controller"] = "Errors";
        rd.Values["action"] = actionName;
        IController c = new ErrorsController();
        c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
    }
}

As result my friendly error pages are rendered. But the Windows authentication dialog does not appear. If the 401 HTTP status code occurs it immediately displays 401 error message. The trick with the <httpErrors> section of web.config file gives same result.

Also, I found proposal to catch the 401.2 HTTP status code that should occur when the dialog is cancelled. But in my case the code never occurs.

How to use user friendly error pages instead of default messages but don't change authentication dialog logic?

I need to implement the following requirements:

  1. The Windows authentication dialog is appeared only for a user, whose AD account doesn't have required permissions, to allow the user provides another credentials.
  2. The dialog is appeared while correct credentials are not provided or user doesn't cancel it.
  3. The custom 401 error page will be displayed only if the user cancels the dialog.
like image 990
Alexander Avatar asked Jul 12 '18 11:07

Alexander


1 Answers

By default the server leaves the response untouched only if the SetStatus flag is set. So, it is necessary to clearly specifiy what IIS must to do with an existing response when the HTTP status code is an error.

One way is to configure the <httpErrors> element of <system.webServer> section in the Web.config file. Just set existingResponse attribute value to PassThrough, so that the server leaves the response untouched if an existing response exists.

<system.webServer>

  <!-- configuration settings -->

  <httpErrors errorMode="Custom" existingResponse="PassThrough" />
</system.webServer>

The configuration prevent error page rendering if the sever prompts for a Windows user account credentials. The response will be replaced with the error page after the dialog cancellation.

The same result could be achieved by disabling of IIS custom errors through TrySkipIisCustomErrors property of HttpResponse. So the addition the following line to code from the question solves the problem:

Context.Response.TrySkipIisCustomErrors = true;

Note. The second way does not work on my production server for some reason. I think, it requires additional server settings.

like image 62
Alexander Avatar answered Nov 18 '22 04:11

Alexander