how can mvc return Unauthorized code without redirecting to LogIn view

10,424

Solution 1

To prevent login page redirection you must set SuppressFormsAuthenticationRedirect property of HttpContext.Response to true;

 HttpContext.Response.SuppressFormsAuthenticationRedirect = true;

Solution 2

What you are experiencing is a hole in ASP.NET MVC (I hope they fix one day).

The standard operating model for ASP.NET is that if a 401 Http Status code is detected, then as you are experiencing, it automatically redirects to the login page, and this happens even if you have come in via an Ajax call. Unfortunately I have also not found any way to change this behaviour.

What I do instead is return an alternative, otherwise unused Http Status Code that I can detect in the client and handle in the appropriate manner.

Therefore within my Authentication Filter, if its an Ajax request I return 449 otherwise the standard 401. Then on the client I can examine the XMLHttpRequest.status and take appropriate action if 449 is detected.

Solution 3

You can create a simple authorization attribute filter (extend the AuthorizeAttribute class) and use it for your access control. Then try something like this:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    if (filterContext.HttpContext.Request.IsAjaxRequest() ||  string.Compare("GET", filterContext.HttpContext.Request.HttpMethod, true) != 0)
    {
        // Returns 403.
        filterContext.Result = new HttpStatusCodeResult((int)HttpStatusCode.Forbidden);
    }
    else
    {
        // Returns 401.
        filterContext.Result = new HttpUnauthorizedResult();
    }
}

The effect is that, POST and AJAX requests will always receive a 403 response which makes it easier for you to handle your ajax submits in javascript. As for the non-ajax posts, it doesn't really matter what the response is because your user shouldn't have got his hands on the submit form in the first place :)

As for the other requests, the method returns 401 that the formsAuthentiction module will pick up and then redirect your response to the login page.

Solution 4

This is the way I managed to prevent redirection to the login page.

In my case when I wanted to receive the status code in order to handle it in javascript :

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302 && Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
    {
        Context.Response.Clear();
        Context.Response.StatusCode = 401;
    }
}
Share:
10,424

Related videos on Youtube

mateo
Author by

mateo

Updated on May 03, 2022

Comments

  • mateo
    mateo almost 2 years

    My MVC web application serves two types of users.

    • First one over standard web browser;
    • Second one over REST returning only JSON data.

    Additionally,

    • Both require Authentication and authorization;
    • Both scenarios are differentiated based on the route so that I know what content to serve.

    When users access the application, if they are not logged in, the application should react differently.

    1. In the first case it should return the default LogIn page (this is fine).
    2. In the second case it should return a 401 Unauthorized code only.

    I'm used to working with WCF REST service where I could raise an exception like this:

    throw new WebProtocolException(System.Net.HttpStatusCode.Unauthorized, exc.Message, exc);
    

    and receive a 401 message. The problem with the same approach within MVC when I put the statusCode like this:

    HttpContext.Response.StatusCode = (Int32)HttpStatusCode.Unauthorized
    

    it always redirects to the LogIn page.

    How can I do this?

    I've tried overriding the AuthorizeAttribute and handling the OnAuthorization function, but still as soon as I set the statusCode to 401 it gets redirected to the LogIn page.

    • Raciel R.
      Raciel R. about 11 years
      @JP This is weird, I have my webapi exposed in my MVC4 application and I don't see this behavior. The System.Web.Http.Authorize attribute used in the ApiControllers uses the Thread.CurrentPrincipal.Identity.IsAuthenticated method while the System.Web.Mvc.Authorize attribute used in the MvcControllers uses the HttpContext.User.Identity.IsAuthenticated. Are you populating the Principal when you login through your login webapi endpoint? How are you identifying subsequent request from the authenticated user? Are you using any token?
  • jamiebarrow
    jamiebarrow over 11 years
    You may find this kind of approach is not applicable if you aren't using Integrated mode for the app pool.
  • Ferran Salguero
    Ferran Salguero over 10 years
    This property is only available in .NET 4.5 (msdn)
  • Ferran Salguero
    Ferran Salguero over 10 years
    Combining it with .ajaxError() you can check the status code to redirect or reload the full page
  • Kambiz Shahim
    Kambiz Shahim over 10 years
    @hellyeah for the previous versions you can see here
  • Jeff
    Jeff over 10 years
    Where do i put this line of code? Do I have to make a custom filter? Or an HTTP Module?
  • Kambiz Shahim
    Kambiz Shahim over 10 years
    @Jeff You can use it in a action method of the controller and in a custom action filter as well.
  • Artyom
    Artyom almost 8 years
    On how to use this here is an interesting article at codeproject.com/Articles/655086/…
  • Zapnologica
    Zapnologica over 5 years
    Is this per request? So I just put this in the action method where I donot want it to return. Ie and ajax data post?
  • Kambiz Shahim
    Kambiz Shahim over 5 years
    @Zapnologica Yes it is
  • Zapnologica
    Zapnologica about 5 years
    Weird, it didnt work for me. I return a new Unatuthorized and it returns a 200 with login screen.