Handle Ajax request with expired forms authentication, in MVC ajax.form

There is a situation where I display a screen dynamically load a partial view using ajax.

And when the authentication expires, user initiates an ajax call, then the action is being called without authorization.

It actually returns a login view inside the DOM object on the page rather than returns a expected content with partial view.

The users will have bad UI experience and get confused.

Here I demonstrate the scenario to show you what exactly happens and what we can do about it.

1. Create an ajax.form which simply returns a partial view.


@using (Ajax.BeginForm("AjaxTestNoHandle", "Home", new AjaxOptions { UpdateTargetId = "testcontainer2"}, new { id="test-form"}))
{ 
    
}

<div id="testcontainer2">
</div>

2. Create the action method with “Authorize” attribute and return a partial view “TestPartial”

  [Authorize]
        public ActionResult AjaxTestNoHandle()
        {
            return PartialView("TestPartial");
        }

3. Run the application and click button “No Redirection Handling”. Because the user is not authenticated and it returns a partial view with the whole “Login” page. (I made login page with black background for displaying purpose)

image

Now let’s create  a customized authorize attribute to handle this situation.

1.  Create a CustomizedAuthorizeAttribute to handle login redirection if authentication is expired.  I only override the action HandleUnauthorizedRequest to detect if request is ajax, return a json result with a redirect url.

    public class CustomizedAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            string url = string.Format("{0}?returnUrl={1}",System.Web.Security.FormsAuthentication.LoginUrl,
                filterContext.HttpContext.Server.UrlEncode(filterContext.HttpContext.Request.RawUrl));
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                var redirectResult = filterContext.Result as RedirectResult;
                if (filterContext.Result is RedirectResult)
                {
                    // It was a RedirectResult => we need to calculate the url
                    var result = filterContext.Result as RedirectResult;
                    url = UrlHelper.GenerateContentUrl(result.Url, filterContext.HttpContext);
                }
                else if (filterContext.Result is RedirectToRouteResult)
                {
                    // It was a RedirectToRouteResult => we need to calculate
                    // the target url
                    var result = filterContext.Result as RedirectToRouteResult;
                    url = UrlHelper.GenerateUrl(result.RouteName, null, null, result.RouteValues, RouteTable.Routes, filterContext.RequestContext, false);
                }
                filterContext.Result = new JsonResult
                {
                    Data = new { Redirect = url },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
            }
            else
            {
                //non-ajax request
                base.HandleUnauthorizedRequest(filterContext);
            }
            
        }

2. Create controller action method with CustomizedAuthorize

        [CustomizedAuthorize]
        public ActionResult AjaxTest()
        {
            return PartialView("TestPartial");
        }

3. Create ajax.form post to “AjaxTest” method.


@using (Ajax.BeginForm("AjaxTest", "Home", new AjaxOptions { UpdateTargetId = "testcontainer" }, new { id="test-form"}))
{ 
    
}

<div id="testcontainer">
</div>

4. Click “Redirection Handling” button on the page without login. In FireBug – Console, you can see a json is returned

{"Redirect":"/Account/Login?returnUrl=%2fHome%2fAjaxTest%3fLength%3d4"}

5. Now let’s add a javascript function to handle the json and redirect to the returned url. Add a OnSuccess event in “AjaxOptions” call “validate”


@using (Ajax.BeginForm("AjaxTest", "Home", new AjaxOptions { UpdateTargetId = "testcontainer", OnSuccess="validate" }, new { id="test-form"}))
{ 
    
}

<div id="testcontainer">
</div>

6. Create javascript function validate.


    function validate(data) {
        if (data.Redirect)
            window.location = data.Redirect;
    }

7. Here you go, all done! When you click the “Redirection Handling” button, it will detect the forms authentication login url and return to the login page inside the ajax request!

Cools!

To download the demo project, go to codeplex https://ajaxrequestauthentic.codeplex.com/

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s