Form Authentication: Disable redirection to the login page

I have an application using ASP.NET Forms Authentication. For the most part, it works great, but I'm trying to add support for a simple API through an .ashx file. I want the ashx file to have additional authentication (i.e. if you do not provide an authentication header, then it just works anonymously). But, depending on what you are doing, I want to require authentication under certain conditions.

I thought it would be easy to respond to the 401 status code if the required authentication was not provided, but it looks like the Autodesk Forms module intercepts this and responds with a redirect to the login page instead. I mean, if my ProcessRequest method looks like this:

 public void ProcessRequest(HttpContext context) { Response.StatusCode = 401; Response.StatusDescription = "Authentication required"; } 

Then instead of getting the 401 error code on the client, as I expect, I get a 302 redirect to the login page.

For nornal HTTP traffic, I see how it would be useful, but for my API page, I want 401 to go through unchanged so that the client client can respond to it programmatically.

Is there any way to do this?

+69
forms-authentication
May 15 '10 at 8:16
source share
10 answers

ASP.NET 4.5 added the Boolean property HttpResponse.SuppressFormsAuthenticationRedirect .

 public void ProcessRequest(HttpContext context) { Response.StatusCode = 401; Response.StatusDescription = "Authentication required"; Response.SuppressFormsAuthenticationRedirect = true; } 
+74
May 30 '13 at 21:18
source share

After a little research, it looks like FormsAuthenticationModule adds a handler for the HttpApplicationContext.EndRequest event. In this handler, it checks the status code 401 and basically executes Response.Redirect(loginUrl) . As far as I can tell, there is no way to undo this behavior if you want to use the FormsAuthenticationModule .

How I got around this by disabling FormsAuthenticationModule in the web.config file like this:

 <authentication mode="None" /> 

And then implementing Application_AuthenticateEvent itself:

 void Application_AuthenticateRequest(object sender, EventArgs e) { if (Context.User == null) { var oldTicket = ExtractTicketFromCookie(Context, FormsAuthentication.FormsCookieName); if (oldTicket != null && !oldTicket.Expired) { var ticket = oldTicket; if (FormsAuthentication.SlidingExpiration) { ticket = FormsAuthentication.RenewTicketIfOld(oldTicket); if (ticket == null) return; } Context.User = new GenericPrincipal(new FormsIdentity(ticket), new string[0]); if (ticket != oldTicket) { // update the cookie since we've refreshed the ticket string cookieValue = FormsAuthentication.Encrypt(ticket); var cookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName] ?? new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue) { Path = ticket.CookiePath }; if (ticket.IsPersistent) cookie.Expires = ticket.Expiration; cookie.Value = cookieValue; cookie.Secure = FormsAuthentication.RequireSSL; cookie.HttpOnly = true; if (FormsAuthentication.CookieDomain != null) cookie.Domain = FormsAuthentication.CookieDomain; Context.Response.Cookies.Remove(cookie.Name); Context.Response.Cookies.Add(cookie); } } } } private static FormsAuthenticationTicket ExtractTicketFromCookie(HttpContext context, string name) { FormsAuthenticationTicket ticket = null; string encryptedTicket = null; var cookie = context.Request.Cookies[name]; if (cookie != null) { encryptedTicket = cookie.Value; } if (!string.IsNullOrEmpty(encryptedTicket)) { try { ticket = FormsAuthentication.Decrypt(encryptedTicket); } catch { context.Request.Cookies.Remove(name); } if (ticket != null && !ticket.Expired) { return ticket; } // if the ticket is expired then remove it context.Request.Cookies.Remove(name); return null; } } 

This is actually a bit more complicated, but I basically got the code by looking at the implementation of the FormsAuthenticationModule in the reflector. My implementation differs from the built-in FormsAuthenticationModule in that it does nothing if you answer 401 - you do not redirect to the login page at all. I think if it ever becomes a requirement, I can put the item in context to disable auto-redirection or something else.

+35
May 15 '10 at 11:11
source share

I'm not sure if this will work for everyone, but in IIS7 you can call Response.End () after you set the status code and description. Thus, # & $ ^ # @ *! FormsAuthenticationModule will not redirect.

 public void ProcessRequest(HttpContext context) { Response.StatusCode = 401; Response.StatusDescription = "Authentication required"; Response.End(); } 
+11
Nov 03 '10 at 4:02
source share

To work a bit on zacharydl, I used this to solve my problems. For each request, first, if it is AJAX, immediately suppresses the behavior.

 protected void Application_BeginRequest() { HttpRequestBase request = new HttpRequestWrapper(Context.Request); if (request.IsAjaxRequest()) { Context.Response.SuppressFormsAuthenticationRedirect = true; } } 
+7
Jan 03 '14 at 21:27
source share

I don't know how this Response.End () worked for you. I tried this without joy, and then looked at MSDN for Response.End (): "stops page execution and raises the EndRequest event".

Why was it worth my hack:

 _response.StatusCode = 401; _context.Items["401Override"] = true; _response.End(); 

Then in Global.cs add the EndRequest handler (which is called after the HTTP authentication module):

 protected void Application_EndRequest(object sender, EventArgs e) { if (HttpContext.Current.Items["401Override"] != null) { HttpContext.Current.Response.Clear(); HttpContext.Current.Response.StatusCode = 401; } } 
+5
Nov 30 '10 at 16:20
source share

I know that there is already an answer with a tick, but when I tried to solve a similar problem, I found this ( http://blog.inedo.com/2010/10/12/http-418-im-a-teapot-finally-a- % e2% 80% 9clegitimate% e2% 80% 9d-use / ).

Basically you return your own HTTP status code (e.g. 418) to your code. In my case, the WCF data service.

 throw new DataServiceException(418, "401 Unauthorized"); 

Then use the HTTP module to process it in the EndRequest event to rewrite the code to 401.

 HttpApplication app = (HttpApplication)sender; if (app.Context.Response.StatusCode == 418) { app.Context.Response.StatusCode = 401; } 

The browser / client will receive the correct content and status code, it works fine for me :)

If you are interested in learning more about the status of status code 418, see this question and answer .

+5
Jan 06 2018-11-11T00:
source share

what you found out is true for auth forms intercepting 401 and redirecting, but we can also do this to undo this.

Basically you need the http module to intercept the 302 redirect to the login page and cancel it before 401.

This procedure is explained in here.

This link refers to the WCF service, but it is the same in all form scripting scripts.

As explained in the link above, you also need to clear the http headers, but remember to return the cookie header back in response if the original response (i.e. before interception) contains cookies.

+4
Nov 15 2018-10-11T00:
source share

This is a known issue, and there is a NuGet package for that and / or source code .

+2
Dec 13 '11 at 19:53
source share

You do not set the WWW-Authenticate header in the code you display, so the client cannot perform HTTP authentication instead of forms authentication. If so, you should use 403 instead of 401, which will not be intercepted by the FormsAuthenticaitonModule .

0
Nov 30
source share

View the Web.config file in configuration\authentication . If there is a forms loginUrl with the loginUrl attribute, delete it and try again.

-2
May 15 '10 at 8:26 a.m.
source share



All Articles