In Asp.Net MVC 2, there is a better way to return 401 status codes without getting a redirect outout

I have a part of my site that has a lightweight xml / json REST API. Most of my site is behind auth forms, but authentication is required to complete some of my API actions.

I have my own AuthorizeAttribute attribute for my API, which I use to check for specific permissions, and when it fails, it leads to error 401. Everything is fine, except that I use auth forms, Asp.net conveniently converts this to 302 redirects to my login page.

I saw some previous questions that seem a bit hacky to either return 403 or put some logic in global.asax protected void Application_EndRequest () which will essentially convert 302 to 401, where it meets any criteria.

  • Previous question
  • Previous Question 2

What I'm doing right now seems like one of the questions, but instead of checking Application_EndRequest () for 302, I return my authorize attribute 666 which tells me that I need to set this to 401.

Here is my code:

protected void Application_EndRequest() { if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS) { //check for 666 - status code of hidden 401 Context.Response.StatusCode = 401; } } 

Even if this works, my question is there is something in Asp.net MVC 2, what would prevent me from doing this? Or, in general, is there a better way? I would think that it would be very useful for those who make REST api or just people who execute ajax requests in their controllers. The last thing you want is to make a request and get the contents of the login page, not json.

+7
rest asp.net-mvc asp.net-mvc-2
source share
5 answers

I still use the query ending technique, so I decided that I would do this answer, but actually any of the options listed here , as a rule, I would say that these are the best answers.

 protected void Application_EndRequest() { if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS) { //check for 666 - status code of hidden 401 Context.Response.StatusCode = 401; } } 
0
source share

How to decorate your controller / actions with a custom filter:

 [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] public class RequiresAuthenticationAttribute : FilterAttribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationContext filterContext) { var user = filterContext.HttpContext.User; if (!user.Identity.IsAuthenticated) { filterContext.HttpContext.Response.StatusCode = 401; filterContext.HttpContext.Response.End(); } } } 

and in your controller:

 public class HomeController : Controller { public ActionResult Index() { return View(); } [RequiresAuthentication] public ActionResult AuthenticatedIndex() { return View(); } } 
+5
source share

Another way to do this is to implement a custom ActionResult . In my case, I still wanted to, because I need an easy way to send data with custom headers and response codes (for the REST API.) I found the idea of ​​making DelegatingActionResult and just added the Response.End() call to it. Here is the result:

 public class DelegatingActionResult : ActionResult { public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); Command(context); // prevent ASP.Net from hijacking our headers context.HttpContext.Response.End(); } private readonly Action<ControllerContext> Command; public DelegatingActionResult(Action<ControllerContext> command) { if (command == null) throw new ArgumentNullException("command"); Command = command; } } 
+2
source share

The simplest and cleanest solution I have found for this is to register a callback with the jQuery.ajaxSuccess () event and check the response header "X-AspNetMvc-Version".

Every jQuery Ajax request in my application is handled by Mvc, so if there is no header, I know that my request is redirected to the login page and I just reload the page to redirect to the top level:

  $(document).ajaxSuccess(function(event, XMLHttpRequest, ajaxOptions) { // if request returns non MVC page reload because this means the user // session has expired var mvcHeaderName = "X-AspNetMvc-Version"; var mvcHeaderValue = XMLHttpRequest.getResponseHeader(mvcHeaderName); if (!mvcHeaderValue) { location.reload(); } }); 

A page reload may cause some Javascript errors (depending on what you are doing with the Ajax response), but in most cases when there is no debugging, the user will never see them.

If you do not want to use the inline header, I am sure that you can easily add a custom one and follow a single template.

0
source share

TurnOffTheRedirectionAtIIS

From MSDN, This article explains how to avoid redirecting 401 responses :).

Citation:

Using IIS Manager, right-click WinLogin.aspx, click Properties, and then click the Custom Errors tab. Change various 401 errors and assign a custom redirect. Unfortunately, this redirect must be a static file - it will not be processed by an ASP.NET page. My solution is to redirect to a static Redirect401.htm file, with a full physical path that contains javascript, or a meta tag to redirect to a real ASP.NET login form named WebLogin.aspx. Please note that you lose the original ReturnUrl in these redirects, because the IIS redirect error requires a static html file without anything dynamic, so you will have to handle this later.

Hope this helps you.

0
source share

All Articles