The problem here is that when the session security token expires the user would lose all their progress because there is a passive redirect to the STS (secure token service). Luckily I found an article describing how to check the token after each HTTP request and renew it if it has reached its half-life (http://brockallen.com/2013/02/17/sliding-sessions-in-wif-with-the-session-authentication-module-sam-and-thinktecture-identitymodel/).
The sliding security token functionality solves half of the problem. The user could still lose their progress on the form if they don’t post back before the session token expires. To remedy this, I created a warning dialog that will extend the session if they click the button, or it’ll save a draft of the course if they don’t.
Sliding the session token
By hooking into the ‘session security token received’ event in the session authentication module (global.asax), you can check if the session token has passed its half-life and renew its expiration date. This event is fired at each HTTP request.
protected void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
{
SessionAuthenticationModule sam = FederatedAuthentication.SessionAuthenticationModule;
var token = e.SessionToken;
var duration = token.ValidTo.Subtract(token.ValidFrom);
if (duration <= TimeSpan.Zero) return;
var diff = token.ValidTo.Add(sam.FederationConfiguration.IdentityConfiguration.MaxClockSkew).Subtract(DateTime.UtcNow);
if (diff <= TimeSpan.Zero) return;
var halfWay = duration.TotalMinutes / 2;
var timeLeft = diff.TotalMinutes;
if (timeLeft <= halfWay)
{
e.ReissueCookie = true;
e.SessionToken =
new SessionSecurityToken(
token.ClaimsPrincipal,
token.Context,
DateTime.UtcNow,
DateTime.UtcNow.Add(duration))
{
IsPersistent = token.IsPersistent,
IsReferenceMode = token.IsReferenceMode
};
}
}
Auto-save form before token expiration
I added a partial razor view that will initialize the dialog two minutes before the session expires, then the user has two minutes to extend the session or the course is saved as a draft. I added this partial view to my form in a div with id ‘sessionHandler’.
@{
var beforeEndSession = 60000 * (Session.Timeout – 2);
}
<script>
var saveDraft = function () {
$(‘input[name=”autoSave”]’).val(“true”);
$(‘input[name=”Draft”]’).click();
};
var myWarning = function () {
var autoSave = window.setTimeout(saveDraft, 120000);
$(‘#extend-session’).dialog({
dialogClass: “session-warning”,
width: “auto”,
resizable: false,
modal: true
});
$(‘#extend-session input’).click(function() {
$(‘#extend-session’).dialog(‘destroy’);
window.clearTimeout(autoSave);
});
};
window.setTimeout(myWarning, @beforeEndSession);
</script>
<div id=”extend-session” >
<span>Your session is about to expire</span>
@using (Ajax.BeginForm(“ContinueSession”, “Courses”, null, new AjaxOptions {UpdateTargetId = “sessionHandler”, InsertionMode = InsertionMode.Replace}))
{
<input type=”submit” value=”Extend Session”/>
}
</div>
If the ‘Extend Session’ button isn’t clicked after two minutes (120,000 milliseconds), the ‘saveDraft’ function is called which sets the auto-save flag to true and submits the form. The auto-save flag triggers logic in the controller to bypass validation and notify the user. If the ‘Extend Session’ button is clicked, the auto-save timer is cleared and an Ajax call returns the same partial, refreshing the session.
public ActionResult ContinueSession(bool isEditTimeout)
{
return PartialView(“_EditTimeout”);
}