15

I have an ASP.NET web-site with authentication using ActiveDirectory.

Now, when an authenticated user opens a page - he is automatically authenticated. I have faced a problem - when a non-authenticated user (for example, a Mozilla Firefox user with not defined network.automatic-ntlm-auth.trusted-uris property) opens a page, IIS sends 401 response and prompts for a login \ password.

What I want is not to prompt him for a login \ password - just show a custom error page. It sounds pretty simple - authenticated users get the requested page, non-authenticated are redirected to a custom error page. It would work fine for FormsAuthentication.

However, I have tried so many ways by now. Any Web.config redirects are not working. Even if I clear a Response and put there a redirect - I will get a loop because this custom page (*e.g., /Error/AccessDenied) also requires authentication. Marking a controller as AllowAnonymous does nothing.

However, if I enable Anonymous authentication in IIS Manager, real authenticated domain users are not being authorized when they open a web-site.

How can I solve this problem?

Yeldar Kurmangaliyev
  • 33,467
  • 12
  • 59
  • 101
  • You want non-IE users to be redirect to another page? URL rewriting based on User Agent is the way to go. – Lex Li Aug 21 '15 at 14:05
  • Not possible. First request from browser is always anonymous. IIS always responds with `401 Unauthorized` with `www.authenticate: negotiate` (or NTLM or both) header. The client (browser) then requests again with `Authorization: Negotiate ..hash..` header this time. Depending on trusted-site, browser ***will*** always ask for credentials. You cannot show a custom error, because the first response is always `401`, regardless. – Abhitalks Aug 24 '15 at 06:12
  • You could write an HTTP module and hook on HttpApplication.PostAuthenticateRequest. From there you can use HttpContext.RewritePath and/or pretty much play with the response context the way you want. Difficult to say more w/o a clear repro case. – Simon Mourier Aug 24 '15 at 08:35
  • @Abhitalks As your answer helped me the most, I would like to give a bounty to it. Could you, please, post it as the answer? – Yeldar Kurmangaliyev Aug 24 '15 at 09:53
  • 1
    @YeldarKurmangaliyev: Don't worry about the bounty. Getting your problem solved is more important. Please accept your answer. It is nicely written and conveys the important parts well. Alternatively, just wait out the bounty period, You never know, you might get a better answer. :) – Abhitalks Aug 24 '15 at 10:27
  • @Abhitalks is it possible that I can have all system generated error within my application layout page. I don't want custom page for diffrent error. I just want to display system generated message as is, but within my _layout.cshtml page. Thanks. – ary Jan 04 '16 at 22:17

3 Answers3

23

Thanks to @Abhitalks for explaining how it works in comments. I don't know why, but I was sure that IE and Google Chrome are sending authorization header on the first request and, that's why, only unauthorized users get 401 response. After I have understood that I cannot avoid 401 response at all, I have decided to use this simple approach as this behaviour is the closest to desirable.

I have added the following method in Global.asax:

protected void Application_EndRequest(object sender, EventArgs e)
{
    if (Response.StatusCode == 401)
    {
        Response.ClearContent();
        Response.WriteFile("~/Static/NotAuthorized.html");
        Response.ContentType = "text/html";
    }
}

Now, when a user opens a page, the server returns him a custom error page but with 401 Unauthorized header.

Chrome, IE or well-configured Firefox. Users requests an URL, the server returns the error page with 401 header - a browser automatically completes authorization challenge, redirects to the same URL, the server returns the correct page and 200 now. The user will not see this error page.

Non-configured Firefox. User requests an URL, the server returns the error page with 401 header - a browser cannot complete authorization challenge and prompts user for credentials.

  • User enters correct login. User requests the same URL again, gets a page and 200 OK.

  • User enters incorrect login. A browser prompts for credentials again.

  • User presses Cancel. A browser displays the custom error page which has been sent with header 401. This page tells the user that if he is using Firefox, then he should either enter his credentials or allow automatic NTLM authentication.

Yeldar Kurmangaliyev
  • 33,467
  • 12
  • 59
  • 101
3

Important addition to Yeldar's comment:

When altering the response message for remote requests (read: non-localhost) you will need to add the following to your config file:

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

If you do not allow the response to "pass through" remote clients will get the default "You do not have permission to view this directory or page".

I got this info from: https://stackoverflow.com/a/17324195/3310441

KoenW
  • 453
  • 1
  • 3
  • 13
0

Override the HandleUnauthorizedRequest method in Authorize attribute.

Ex:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
   filterContext.Result = new RedirectToRouteResult(
                            new RouteValueDictionary (new {controller = "CustomError", action = "Unauthorized"}));
}
Sajithd
  • 529
  • 1
  • 5
  • 11