Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<h2>Solution 1, return response indicating error</h2> <p><br> <a href="http://apress.com/book/view/1430228865" rel="nofollow noreferrer">Pro ASP.NET MVC 2 Framework</a> offers one solution for this</p> <blockquote> <p>If an Ajax request is denied authorization, then usually you don’t want to return an HTTP redirection to the login page, because your client-side code is not expecting that and may do something unwanted such as injecting the entire login page into the middle of whatever page the user is on. Instead, you’ll want to send back a more useful signal to the client-side code, perhaps in JSON format, to explain that the request was not authorized. You could implement this as follows:</p> </blockquote> <pre><code>public class EnhancedAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext context) { if (context.HttpContext.Request.IsAjaxRequest()) { UrlHelper urlHelper = new UrlHelper(context.RequestContext); context.Result = new JsonResult { Data = new { Error = "NotAuthorized", LogOnUrl = urlHelper.Action("LogOn", "Account") }, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } else base.HandleUnauthorizedRequest(context); } } </code></pre> <p>You'd then get back a JSON object from the controller <code>{'Error': 'NotAuthorized', 'LogonUrl': '...'}</code> which you could then use to redirect the user.</p> <p>And alternative response, if you're expecting HTML, could be to return a plain string, like <code>NotAuthorized:&lt;your_url&gt;</code> and check if the response matches this pattern.</p> <p><br></p> <h2>Solution 2, return and handle <code>401 Unautorized</code> status code</h2> <p><br> As this has to be checked on every ajax request's <code>success</code> callback, this gets quite tedious. It would be nice to be able to catch this case globally. The best solution would be to return a <code>401 Unauthorized</code> status code from the server and use jQuery's <a href="http://api.jquery.com/ajaxError/" rel="nofollow noreferrer"><code>.ajaxError</code></a> to catch it, but this is problematic on IIS/ASP.NET, since it's deadly committed to redirecting you to the login page if the response has this statuscode. As long as the <code>&lt;authentication&gt;</code> tag is present in <code>web.config</code> this redirection will happen <a href="https://stackoverflow.com/questions/123726/401-response-code-for-json-requests-with-asp-net-mvc/130314#130314"><sup>[1]</sup></a> if you don't do something about it.</p> <p>So let's do something about it. <a href="https://stackoverflow.com/questions/123726/401-response-code-for-json-requests-with-asp-net-mvc/1072996#1072996">This</a> hacky way seemed nice. In your global.asax.cs</p> <pre><code>protected void Application_EndRequest() { if (Context.Response.StatusCode == 302 &amp;&amp; Context.Request.RequestContext.HttpContext.Request.IsAjaxRequest()) { Context.Response.Clear(); Context.Response.StatusCode = 401; } } </code></pre> <p>(I'd love to hear it if anyone knows of any better methods to return a <code>401</code> status code.)</p> <p>By doing this, you're preventing the default behaviour of redirecting to the logon page when the request is an ajax request. You can therefore use the default <code>AuthorizeAttribute</code> as it is, since <code>Application_EndRequest</code> takes care of the rest.</p> <p>Now, in the jQuery code we'll use the <code>.ajaxError</code> function to catch the error. This is a global ajax event handler, meaning it will intercept every error made by any ajax call. It can be attached to any element - in my example I've just chosen <code>body</code> cause its always present</p> <pre><code>$("body").ajaxError(function(event, XMLHttpRequest, ajaxOptions, thrownError) { if (XMLHttpRequest.status == 401) { alert("unauthorized"); } }); </code></pre> <p>This way you get your redirection logic centralized in <em>one</em> place, instead of having to check it on every damn ajax request success callback</p> <p><br></p> <h2>Solution 3, return custom header</h2> <p><br> <a href="https://stackoverflow.com/questions/199099/how-to-manage-a-redirect-request-after-a-jquery-ajax-call/579895#579895">This answer</a> offers an alternative solution. It uses the same kind of global event handler, but skips the hacky bits. It adds a custom header to the page if you're not authenticated and the request is an ajax request, and checks for this header on each <a href="http://api.jquery.com/ajaxComplete/" rel="nofollow noreferrer"><code>.ajaxComplete</code></a>. Note that the method provided in the linked answer is unsecure, as it will return the original view with possibly sensitive content and rely on javascript to redirect the user away from it. With a bit of modification it's quite awesome as it doesn't require any hacks and the logic can be centralized to one callback function. The only downside as I can see is that it doens't reply with the semantically correct status code, as it's actually not <code>200 OK</code> because you're <code>401 Unauthorized</code></p> <p><br> We can bake this into another custom <code>EnhancedAuthorizationAttribute</code></p> <pre><code>public class EnhancedAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext context) { if (context.HttpContext.Request.IsAjaxRequest()) { context.HttpContext.Response.AddHeader("REQUIRES_AUTH", "1"); context.Result = new EmptyResult(); } else { base.HandleUnauthorizedRequest(context); } } } } </code></pre> <p>Now, every time an ajax request completes, you check for this header:</p> <pre><code>$('body').ajaxComplete(function(event,request,settings){ if (request.getResponseHeader('REQUIRES_AUTH') === '1'){ alert("unauthorized"); }; }); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload