Authorize attribute and jquery AJAX in asp.net MVC
Asked Answered
T

4

35

I have used jquery ajax function to submit a form. The users have to be logged in else they must redirect to a login page.I have used Authorize() attribute for it.

[Authorize]
public ActionResult Creat()
{
....
}

If the user is not login the action return login page to jquery's ajax functions and it is displayed on the same page but I want to redirect the user to login page. Is there any solution?

Tracy answered 10/3, 2011 at 11:0 Comment(0)
V
91

Working example: https://github.com/ronnieoverby/mvc-ajax-auth

Important parts:

AjaxAuthorizeAttribute:

using System.Web.Mvc;

namespace MvcApplication1
{
    public class AjaxAuthorizeAttribute : AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(AuthorizationContext context)
        {
            if (context.HttpContext.Request.IsAjaxRequest())
            {
                var urlHelper = new UrlHelper(context.RequestContext);
                context.HttpContext.Response.StatusCode = 403;
                context.Result = new JsonResult
                {
                    Data = new
                    {
                        Error = "NotAuthorized",
                        LogOnUrl = urlHelper.Action("LogOn", "Account")
                    },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
            }
            else
            {
                base.HandleUnauthorizedRequest(context);
            }
        }
    }
}

Javascript:

    $(function () {
        $(document).ajaxError(function (e, xhr) {
            if (xhr.status == 403) {
                var response = $.parseJSON(xhr.responseText);
                window.location = response.LogOnUrl;
            }
        });
    });

Use the attribute in a controller:

    [AjaxAuthorize]
    public ActionResult Secret()
    {
        return PartialView();
    }

Do some ajax:

@Ajax.ActionLink("Get Secret", "Secret", new AjaxOptions { UpdateTargetId = "secretArea", })

<div id="secretArea"></div>
Verrocchio answered 18/6, 2012 at 15:13 Comment(9)
Totally awesome Ronnie! I love it when a solution just works! Ghooti needs to mark this answer...Halfcaste
@BenPower Thanks for the encouraging words! I'm glad I could help.Verrocchio
I was using formsauthentication redirect and had to enter this line of code to prevent the redirect and the correct http status to be returned... filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;Muro
Since Authorization will make a difference (the resource would be available after login) isn't it better to return a 401 instead of 403 error? 403 is Forbidden and 401 is UnauthorizedGomulka
Instead of calling window.location = response.LogOnUrl;, I use window.location.reload(); so I can get a redirect link in the URL. This may or may not be suitable for your application.Kodiak
Will the [AjaxAuthorize] attribute on the action override an [Authorize] attribute on the whole controller?Seamark
@Odys: No, this will NOT work with a 401 status code. The ASP.net pipeline interferes with 401 before this kicks in. Stick with 403.Elenor
@DeepakMurali Well, damn. Where I come from 200's are a good thing.Verrocchio
Very nice. Thank you, Ronnie.Virulent
O
7

Just a handy addition to #Ronnie's answer

if you want to keep the page url on redirect.

 var pathname = window.location.pathname;
        if (xhr.status == 403) {
                var response = $.parseJSON(xhr.responseText);
                window.location = response.LogOnUrl + '?ReturnUrl=' + pathname;
            }
Orts answered 6/10, 2015 at 14:6 Comment(1)
Easier solution imo: returnUrl = context.HttpContext.Request.UrlReferrer.LocalPath for routeValues of urlHelper.ActionChape
G
0

As another extension to Ronnie Overby's answer.

His solution doesn't work with webapi, but this is fine because you can use normal Authorize attribute instead and then handle the 401 status in the ajaxError function as follows.

    $(document).ajaxError(function (e, xhr) {
    //ajax error event handler that looks for either a 401 (regular authorized) or 403 (AjaxAuthorized custom actionfilter). 
    if (xhr.status == 403 ||xhr.status == 401) {
       //code here
    }
});
Gomar answered 14/12, 2016 at 12:53 Comment(0)
J
0

If you will observe in the network tab, you will see that the despite the status code being a 200, the server responds with X-Responded-JSON which can be parsed to get the actual server status code response which is 401 in this case so you can handle the response from the Authorize attribute in your AJAX call like this in your .cshtml View file:

$.ajax({
    url: '@Url.Action("MyMethod", "MyController")',
    type: "POST",
    dataType: "html",
    data: myData,
    success: function (data, textStatus, xhr) {
        //For handling 401 with Authorize attribute on Controller
        if (xhr.getResponseHeader("X-Responded-JSON") != null && JSON.parse(xhr.getResponseHeader("X-Responded-JSON")).status == "401") {
            alert("Unauthorized access returned by Controller");
            //Will redirect to login page
            location.reload(true);
        }
        else {
            alert("Still Authorized");
        }
    },
    error: function (err) {
        alert("There was a technical error");
        console.log(err)
    },
});
Janicejanicki answered 18/3 at 20:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.