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?

Ghooti Farangi
  • 19,926
  • 15
  • 46
  • 61

3 Answers3

92

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>
Ronnie Overby
  • 45,287
  • 73
  • 267
  • 346
  • 3
    Totally awesome Ronnie! I love it when a solution just works! Ghooti needs to mark this answer... – Ben Power Sep 24 '13 at 02:25
  • @BenPower Thanks for the encouraging words! I'm glad I could help. – Ronnie Overby Sep 24 '13 at 15:41
  • 3
    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; – TWilly Jan 24 '14 at 22:14
  • 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 Unauthorized – Odys Sep 06 '14 at 09:51
  • 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. – mellis481 Mar 31 '15 at 14:41
  • 1
    Will the [AjaxAuthorize] attribute on the action override an [Authorize] attribute on the whole controller? – Greylander Jul 21 '15 at 18:59
  • 1
    @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. – Bruce Pierson Apr 29 '16 at 20:16
  • 1
    @DeepakMurali Well, damn. Where I come from 200's are a good thing. – Ronnie Overby Jul 03 '17 at 15:47
  • Very nice. Thank you, Ronnie. – Ian Gesner Sep 25 '17 at 21:43
8

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;
            }
Mudasar Rauf
  • 544
  • 7
  • 18
  • Easier solution imo: `returnUrl = context.HttpContext.Request.UrlReferrer.LocalPath` for routeValues of `urlHelper.Action` – Martin Dawson Jun 03 '16 at 23:00
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
    }
});
Tom Widdowson
  • 33
  • 1
  • 6