Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ADFS session expires and causes error

Tags:

ajax

adfs

We use ADFS for our internal applications - users are basically logged in transparently anytime they go to one of our apps. However, if a user leaves a page open for over an hour then tries to do something on that page (other than navigate to another page), they get an error:

This page is accessing information that is not under its control. This poses a security risk. Do you want to continue?

It seems like the page is trying to redirect that request to the ADFS server and that is being prevented by the browser.

My question is thus: How do I catch this situation and get the user to the ADFS server to reauthenticate?

I haven't had any luck finding anything on Google regarding this.

like image 468
zimdanen Avatar asked Mar 27 '13 20:03

zimdanen


People also ask

What causes a session to expire?

If your Internet connection is unstable, periodically disconnecting and reconnecting, it can cause a website session to expire. When the Internet connection is lost the website connection can be terminated, resulting in a session expired message if you try to access any page after the Internet reconnects.

How do I troubleshoot ADFS login problems?

Check the client browser of the user Check the following settings in Internet Options: On the Advanced tab, make sure that the Enable Integrated Windows Authentication setting is enabled. Following Security > Local intranet > Sites > Advanced, make sure that the AD FS URL is in the list of websites.

What happens when a session expires?

Session timeout represents the event occuring when a user does not perform any action on a web site during an interval (defined by a web server). The event, on the server side, changes the status of the user session to 'invalid' (ie.


1 Answers

Update: The below solution depends on iframes. ADFS 3.0 has X-Frame-Options defaulted to DENY, with no option to change the setting. So this solution will only work on ADFS 2.1 & earlier.

In your global.asax.cs, you're going to want to catch any mid-AJAX 302s and turn them into a 401 Unauthorized. This will prevent the call from proceeding (and popping that message), and will send us to $(document).ajaxError().

    protected void Application_EndRequest()
    {
        var context = new HttpContextWrapper(this.Context);
        if (context.Response.StatusCode == 302 && context.Request.IsAjaxRequest())
        {
            context.Response.Clear();
            context.Response.StatusCode = 401;
        }
    }

Then, in there, intercept any 401s before they proceed to the rest of your error handling. I chose to show a message to the users. You can do the next step right here, but for readability, I'm sending the ajaxSettings object to another function. Return true so it won't proceed into the rest of your error handling.

If you want to doublecheck that this is ADFS, event.target.referrer will have the URL of the attempted redirect.

$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
    if (xhr.status == 401) { 
        alert("Your session has timed out. Click OK to reauthorize and extend your session.");

        TriggerReauthenticationRefresher(ajaxSettings); 
        return true;
    }
…the rest of the error handling code…            
});

I have an empty div in my page just for this situation, with an id of 'refresherBox', but you can do this on any element in your DOM. Put together an iframe that goes to some dummy page in your domain. In my case, the contents of ADFSRefresher.cshtml are just

 <div><input type="hidden" value="@DateTime.Now.ToString()" /></div>

Instead of using global variables, I'm storing the ajaxSettings using .data(). We also need to keep track of how many times the iframe reloads, so we're also storing loadcount. Insert the iframe into the DOM, and it will kick off.

function TriggerReauthenticationRefresher(ajaxSettings) {
    var refreshframe = '<iframe src="@Url.Action("ADFSRefresher", "Debug")" style="display:none" onload="TrackFrameReloads()" />';

    $('#refresherBox').data('loadcount', 0);
    $('#refresherBox').data('originalRequestSettings', ajaxSettings);

    $('#refresherBox').html(refreshframe);
}

TrackFrameReloads will fire every time the iframe finishes loading. Since we know there is an impending ADFS redirect, it will fire twice. The first time will be the redirect, and the second time will be to its src url. So the first time it fires, we just increment loadcount.

The second time it fires, we know we have been successfully reauthenticated. Retrieve the ajaxSettings, clear the stored data, and you can then re-use your original settings to send the AJAX call! It will go through, un-redirected, and run its original success & complete functions.

function TrackFrameReloads() {
    var i = $('#refresherBox').data('loadcount');
    if (i == 1) {
        alert('Your session has been extended.');

        var ajaxSettings = $('#refresherBox').data('originalRequestSettings');

        $('#refresherBox').removeData();

        $.ajax(ajaxSettings);

    } else {
        $('#refresherBox').data("loadcount", 1);
    }
}

Be aware that if you defined them, the error and complete functions will have already fired.

You can skip the two alert messages to the users if you like. Depending on your ADFS setup, this should only take 1 second, and the user doesn't have to be informed that any of this happened at all!

like image 111
friggle Avatar answered Sep 29 '22 10:09

friggle