Question

I'm going to try an explain this as best as possible. So, here is my issue.

I have a controller that has two actions (we will call this Controller1 with an Index action) one uses the HttpGet attribute, the other has the HttpPost attribute and both use the Authorize attribute to make sure someone is authenticated.

The Controller1.Index view uses a Partial View which calls a View called _Commands (also on the Controller1 controller). This partial view also has the Authorize attribute associated with it in the controller.

If the user isn't authorized, they are forced to the Login controller (LoginController) which has a Get and Post attributes (Index action) associated with it.

The Controller1.Index calls the partial view through an action called GetRecord. If the record exists, it renders the Partial (returns a PartialView), if it doesn't it pretty much just renders a blank DIV. This is all done through Jquery .ajax calls using POST ajax calls.

However, the issue is when a user sits on the page for a large amount of time and their session expires, and he does an jquery event (say change a drop down that fires the ajax call to update the partial), the partial is rendered with the Login.Index view (and calls the LoginController.Index GET action), where the _Command partial should be.

Now my question is, how can I force the login screen to "kick itself" out of any partial call and then reload as the top page?

I've tried a couple things that DID NOT work. For instance, I tried Request.IsAjaxRequest in the Login GET and it had no effect. I also tried IsChildAction, with no effect. Both returned FALSE.

Anyone else have any ideas how I can work around this issue?

Was it helpful?

Solution

Here's what has worked for me on recent projects.

First, in a global js file I setup the following:

$.ajaxSetup({
        error: function (xhr, textStatus, errorThrown) {
            //make sure this isn't caused by navigating away from the page by checking the xhr readyState
            if (xhr.readyState == 4) {

                switch (xhr.status) {
                    case 401:
                    case 403:
                        // take them to the login but hang on to their current url
                            //calling reload does this by leveraging the rest of our framework automagically!
                        window.location.reload(true);
                        break;
                    default:
                        bootbox.alert('<div class="text-center"><h2>An error was encountered</h2><h3>Sorry, an error has occurred.  The system administrators have been notified.</h3></div>');
                        break;
                }
            }
        }
    });

Obviously a full page reload already handles booting the user back to login, so I just detect a 403 and force the page to reload. There's also some other ajax error handling there, not necessary for what you're requesting.

Now, that 403 isn't the default unauth status, so to make that happen I have a custom auth attribute:

public class AuthorizationRequiredAttribute : AuthorizeAttribute
{
    #region Overrides of AuthorizeAttribute

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var skipAuthorization =
            filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) ||
            filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true);

        if (skipAuthorization) return;

        base.OnAuthorization(filterContext);

        //now look to see if this is an ajax request, and if so, we'll return a custom status code
        if (filterContext.Result == null) return;

        if (filterContext.Result.GetType() == typeof (HttpUnauthorizedResult) &&
            filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new ContentResult();
            filterContext.HttpContext.Response.StatusCode = 403;
        }
    }
    #endregion
}

There are reasons (that I can't recall now, but pretty sure I got the info from somewhere else on SO) that you can't rely on the standard unauth status code and thus must override it with that 403. Hope this helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top