So we have our session timeout as:
<sessionState timeout="1000">
and the forms authentication timeout as:
<forms timeout="1000" loginUrl="Login.aspx" name=".ASPXFORMSAUTH">
Unfortunately session timeouts and forms authentication timeouts work completely different. Session timeouts are just that - the amount of time from the last request is when it will timeout. IE upon every request.. the 'timer' is reset to 20 minutes.
In forms authentication - it is not reset upon every request. For 'performance enhancement' reasons so new cookies don't have to get created/encrypted on every single request, this value only gets update when approximately half of the time remains. IE if the timeout is 20 minutes, for the first 10 minutes of requests.. the timer still 'ticks away' on the ticket even if you request every second. It isn't until after 10 minutes have passed AND you request a page again that the ticket is updated. Secondly.. IIS resets do not reset forms authentication tickets, yet they reset sessions.
So here is a scenario that 'breaks' this:
Request a page at 1:00 PM.
IIS Resets at 1:05 - the session is now invalid, however the forms authentication ticket is still VALID.
The user requests a page and their session is gone, and all sorts of errors start occuring.
How to prevent this:
In global.asax.cs define a method that gets called at the beginning of every request. This method will check to make sure that we have a session if the ticket is valid, otherwise it logs the user out.
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
//Only access session state if it is available
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
{
//If we are authenticated AND we dont have a session here.. redirect to login page.
HttpCookie authenticationCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authenticationCookie != null)
{
FormsAuthenticationTicket authenticationTicket = FormsAuthentication.Decrypt(authenticationCookie.Value);
if (!authenticationTicket.Expired)
{
//of course.. replace ANYKNOWNVALUEHERETOCHECK with "UserId" or something you set on the login that you can check here to see if its empty.
if (Session[ANYKNOWNVALUEHERETOCHECK] == null)
{
//This means for some reason the session expired before the authentication ticket. Force a login.
FormsAuthentication.SignOut();
Response.Redirect(FormsAuthentication.LoginUrl, true);
return;
}
}
}
}
}
I don't normally comment on blogs, but man, this saved my backside. Thanks
ReplyDeletePrecise and useful! I like this article.
ReplyDeletethanks a lot,even i never commented
ReplyDeleteGreat post. I come to understand a lot about it.
ReplyDeleteI agree with all of you that this information is pretty useful which definitely deserve for bookmark.
ReplyDeleteSoftware Development Company
Nice one! Smart.Simple.Precise.
ReplyDeleteThank youvery much for the tip. It help me. Your code translated into ASP.NET - VB is:
ReplyDeleteProtected Sub Application_PreRequestHandlerExecute(ByVal sender As Object, ByVal e As EventArgs)
'Only access session state if it is available
If TypeOf (Context.Handler) Is IRequiresSessionState Or TypeOf (Context.Handler) Is IReadOnlySessionState Then
'If we are authenticated AND we dont have a session here.. redirect to login page.
Dim authenticationCookie As HttpCookie = Request.Cookies(FormsAuthentication.FormsCookieName)
If (authenticationCookie IsNot Nothing) Then
Dim authenticationTicket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(authenticationCookie.Value)
If (Not authenticationTicket.Expired) Then
'of course.. replace ANYKNOWNVALUEHERETOCHECK with "UserId" or something
'you set on the login that you can check here to see if its empty.
If (Session(gcSessionUser_IdOsoby) Is Nothing) Then
'This means for some reason the session expired before the authentication ticket. Force a login.
FormsAuthentication.SignOut()
Response.Redirect(FormsAuthentication.LoginUrl, True)
Return
End If
End If
End If
End If
End Sub
Have a nice day!