ADFS session expires and causes an error

We use ADFS for our internal applications - users basically register transparently at any time in one of our applications. However, if the user leaves the page open for more than an hour, then he tries to do something on this page (except for switching to another page), they receive an error message:

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

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

My question is this: how can I catch this situation and make the user on the ADFS server re-check?

I was not lucky to find anything on Google about this.

+7
source share
2 answers

You can check and re-issue security tokens manually in global.asax and use this to create rolling sessions. With sliding sessions, you can delay re-authentication until it becomes “safe” for this (when data is no longer lost due to ADFS redirection).

Inside the SessionSecurityTokenReceived event, you can evaluate the token and request. If the token has expired and the request is the one that will experience data loss during redirection, you can re-issue a new “temporary” token. The new token should have a very short life, long enough so that you can safely fulfill the current request. Then the token expires and is evaluated again at the next request.

protected void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e) { var now = DateTime.UtcNow; SessionSecurityToken token = e.SessionToken; var httpContext = new HttpContextWrapper(this.Context); if (now > token.ValidTo && (httpContext.Request.IsAjaxRequest() || httpContext.Request.HttpMethod == "POST")) { var sessionAuthModule = (SessionAuthenticationModule)sender; e.SessionToken = sessionAuthModule.CreateSessionSecurityToken(token.ClaimsPrincipal, token.Context, now, now.AddMinutes(2), token.IsPersistent); e.ReissueCookie = true; } } 

The ADFS segment will continue to delay re-authentication until the next GET request. Then, finally, a redirection will occur, and the user will be given the correct token of normal lifespan.

+1
source

Update. The solution below depends on iframes. ADFS 3.0 has X-Frame-Options by default for DENY, without the ability to change the setting. Therefore, this solution will only work in ADFS 2.1 and earlier.

In your global.asax.cs file, you will want to catch any AJAX 302 files and turn them into 401 Unauthorized. This will prevent the call from continuing (and a message will appear) and 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, there, intercept any 401s before they move on to the rest of your error handling. I decided to show the message to users. You can take the next step right here, but for readability I am sending an ajaxSettings object to another function. Return true so that it does not go to the rest of your error handling.

If you want to double that it is ADFS, event.target.referrer will have the URL of the redirect attempt.

 $(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 on my page only for this situation with the identifier 'refresherBox', but you can do this on any element of your DOM. Match the 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 save ajaxSettings using .data (). We also need to keep track of how many times the iframe reloads, so we also save the loadcount. Insert the iframe into the DOM and it will start.

 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 completes the download. Since we know that pending ADFS forwarding exists, it fires twice. The first time there will be a redirect, and the second time will be on its src url. So, at the first start, we just increase the load.

The second time it fires, we know that we have successfully passed the retest. Extract ajaxSettings, clear the saved data, and then you can reuse your original settings to send an AJAX call! It will pass, not be redirected, and will fulfill its initial successful and 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); } } 

Remember that if you have identified them, errors and full functions will already be running.

You can skip two warning messages to users if you wish. Depending on your ADFS setup, this only takes 1 second, and the user does not need to report that all this happened at all!

+3
source

All Articles