Sunday, December 20, 2009

Caution with using asp.net session timeout and FormsAuthentication timeout together

Problem: You've set your session timeout to 20 minutes and forms authentication timeout to 20 minutes. However you notice sometimes your session is not valid and yet people are still logged on. how can this be you ask!?!?!

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;
}
}
}
}
}

33 comments:

  1. I don't normally comment on blogs, but man, this saved my backside. Thanks

    ReplyDelete
  2. Precise and useful! I like this article.

    ReplyDelete
  3. thanks a lot,even i never commented

    ReplyDelete
  4. Great post. I come to understand a lot about it.

    ReplyDelete
  5. I agree with all of you that this information is pretty useful which definitely deserve for bookmark.
    Software Development Company

    ReplyDelete
  6. Thank youvery much for the tip. It help me. Your code translated into ASP.NET - VB is:
    Protected 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!

    ReplyDelete
  7. I'm using Fiddler and capture a web request. Then I log out of the web application. If I replay the request inside Fiddler, it still has the old cookie and everything will still run as if they were still logged in.

    Any ideas how to get around this?

    ReplyDelete
    Replies
    1. Set a breakpoint in fiddler prior to the request going to the server, delete your cookie and resume the request

      Delete
  8. I am a beginner sharepoint user.

    I have got the following problem.

    I have a new Sharepoint Server which uses Sharepoint Server 2010. It runs on Windows Server 2008.

    I am using it as a Webserver and need to deploy a timeout for all users who access it for security purposes.

    Currently it is configured to use Windows Based Authentication on port 80 although i have tried to change this and set up a Form Based Authentication (FBA) site on different port and set the timeout settings for this also but it does not work.
    I have tried using the timeout features of IIS and Sharepoint itself with a FBA setup and the user account still does not time out as required.

    I am using FBA with LDAP environments.

    Do I need to create a new Global file and add the code you mentioned in your blog?

    Any help would be appreciated.

    ReplyDelete
  9. I can't reuse this code, unfortunately when the authorization expires, the authenticationCookie is Nothing.

    ReplyDelete
    Replies
    1. when the auth cookie expires, there's no issue. you are brought back to the login page. The assumption is that you have session based code here to ensure upon login you load a fresh session, remove it, or whatever you want to do.

      Delete
  10. Adam,
    With your first scenario, when a user made requests for the first 10 minutes resetting Session cookie on each request but not resetting authentication ticket, and then DID NOT clicked for the next 10 minutes - it seems that authentication ticket would expire, while Session cookie would not expire yet.
    Is it correct? If so, shouldn't we forcibly clear Session cookie in Forms Authentication Timeout event, or maybe - during a next Forms Authentication request (if any)? Otherwise we might use old Session variables with new Forms Authentication and some logic might get out of sync, no?

    ReplyDelete
    Replies
    1. Sorry for the delay, just saw this now. This is a design issue. If you have session values that could cause a problem when they login again, sure, but thats an app by app basis. You could easily clear out the session and start a new one if the session cookie exists or session["someinitializervalue"] is null. Typically I'll see when one uses session, its initialized upon login and then potentially used throughout. Since you can't always control a user's navigation, and values could exist in the session when they access near any url, Id realllly try to limit its use to initializing on login. Session gets a bad performance rap and there's a reason (as easy as it is to use though) since multiple incoming requests that use the session will block for that single user until each one serially finishes. IE three ajax requests come in at near the same time for that user and they use the session object, they will process only one at a time until each request is done because of the lock put on the session object for that user. Just an FYI :)

      Delete
  11. Thanks its really help full post,save my lots of work

    ReplyDelete
  12. .NET is today part of all windows applications. .NET applications are more comprehensive, secured and have less chances of bugs. All applications in windows 8 application development are developed with ASP.NET.

    ReplyDelete
  13. Thank you Adam, The post helped alot..

    ReplyDelete
  14. I have read your blog its very attractive and impressive. I like it your blog.
    Thanks

    Belfast SEO Companies & iPhone Game Development Belfast UK

    ReplyDelete
  15. Are you trying to make money from your websites/blogs via popup advertisments?
    In case you are, have you considered using PopAds?

    ReplyDelete

  16. I have read your blog its very attractive and impressive. I like it your blog.

    Dot Net Training in Chennai Dot Net Training in Chennai .Net Online Training .Net Online Training Dot Net Training in Chennai Dot Net Training in Chennai


    Dot Net Online Training Dot Net Online Training LINQ Online Training LINQ Online Training ASP.NET Online Training ASP.NET Online Training

    ReplyDelete
  17. Very useful article. Thank you!

    I have an asp.net application with forms authentication. It properly works in local computer. On the web server? after a while receiving the error message below in Event Log file.

    Forms Authentication failed for the request. The ticket supplied has expired.
    Event code: 4005

    On web.config





    Recycling the application pools solves the problem. I have to recycle it every few days though.

    Do you think your code can fix this message? Or any other ideas will be appreciated. Thank you!

    ReplyDelete
    Replies


    1. Forms Authentication failed for the request. The ticket supplied has expired.
      Event code: 4005

      On web.config
      authentication mode="forms"
      Forms name="entryform" protection="all" timeout="20" path="/" slidingExpiration="false" loginUrl="login.aspx" defaultUrl="default.aspx"

      sessionstate cookieless="UseCookies" timeout="30"

      Recycling the application pools solves the problem. I have to recycle it every few days though.

      Do you think your code can fix this message? Or any other ideas will be appreciated. Thank you!

      Delete
  18. Very useful article. Thank you!

    I have an asp.net application with forms authentication. It properly works in local computer. On the web server? after a while receiving the error message below in Event Log file.

    Forms Authentication failed for the request. The ticket supplied has expired.
    Event code: 4005

    On web.config





    Recycling the application pools solves the problem. I have to recycle it every few days though.

    Do you think your code can fix this message? Or any other ideas will be appreciated. Thank you!

    ReplyDelete
  19. Thanks! Didn't know this- "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."

    ReplyDelete
  20. thanks

    [url=http://interrspace.com][color=#ecf3f7]วิธีเล่นบาคาร่า[/color][/url]

    ReplyDelete
  21. Excellent!! This solved the problem I was having. I set session to something on successful login and used the same value in the Application_PreRequestHandlerExecute block to check

    ReplyDelete
  22. Adam, thanks so much for this solution. This is the only 1 of about 10 articles I've read (relating to this issue) that actually works. Cheers Lee.

    ReplyDelete
  23. Ever wondered how external business consultants and valuation experts manage to attach a value to a business that you and your employees in most probability understand better? Whether you are part of the operational team or the senior management within a company contemplating buying or selling business divisions Tiny Net Worth a basic understanding of the valuation process and the fundamental concepts involved, can make a huge difference while dealing with finance professionals.

    ReplyDelete
  24. Really useful.
    Thanks.Sooooooooooo thanks

    ReplyDelete

Note: Only a member of this blog may post a comment.