1

I've been tasked with adding functionality to an ASP.NET 4.0 Web Forms application to warn the user shortly before their session ends, and to provide the option either to continue the session, or to end the session.

I have implemented this via a confirmation dialog box which warns the user that the session is going to end soon and giving the option of pressing 'OK' to continue the session, or pressing 'Cancel' to end the session.

On pressing 'cancel', the page redirects to the logout page.

On pressing 'OK', I invoke a JQuery GET request on an empty ASPX page in my application (KeepAlive.aspx). As I understand, when a user makes a request to the page, ASP.NETshould take care of renewing the session - and thus resetting the session timeout.

However, I found that when the user presses OK, the session is not extended, and so it times out. This is despite the GET request apparently being successful (e.g. the callback function is invoked).

The code I used to implement this exists as a JavaScript function which gets invoked via the onload event on the master page - so it's inherited by all the other pages in the application.

var intervalID;

/* Set a timeout interval based on the server timeout value (-10%) */
function setTimeoutInterval() 
{
    /* Session timeout warning dialog */

    // Get session timeout value
    var timeoutMins = "<asp:ContentPlaceHolder id='timeoutPlaceholder' runat='server'><%= Session.Timeout %></asp:ContentPlaceHolder>";

    // Subtract 10% of the timeout value - to give the user a chance to continue the session before it expires
    var remainingTimeMins = Math.ceil(timeoutMins * 0.1);
    var timeoutMins = timeoutMins * 0.9;

    // Convert the timeout value to milliseconds
    var timeout = timeoutMins * 60 * 1000;

    // Set javascript timeout
    intervalID = window.setInterval("displayTimeoutDialog(" + remainingTimeMins + ")", timeout);
}

/* Display a dialog prompting the user to continue the current session or to end the session */
function displayTimeoutDialog(remainingTimeMins) 
{
    var result = confirm("The session will end in ~" + remainingTimeMins +
    " minute(s). Press OK to continue, or Cancel to log out.");

    if (result == true) {
        // Keep the session alive
        alert("Keep alive!");
        $.get("KeepAlive.aspx", function() { alert("Successful request"); });
    }
    else {
        // Redirect to the logout page
        window.location.href("Logout.aspx");
    }
}
Ciaran Gallagher
  • 3,895
  • 9
  • 53
  • 97
  • Why are you setting a JavaScript variable to an asp server control... and then multiplying that by .9? – MikeSmithDev Dec 06 '13 at 14:59
  • 1
    @Mike He's not setting it to a server control. His code will be rendered as: "var timeoutMins = 20" or whatever the setting is. He then multiplies it by .9 so the dialog pops up before the session actually expires. – Stefan Dec 06 '13 at 15:03
  • Yep, the comments explain it. I'm just getting the server timeout value from the server (e.g. 20 minutes), then setting an interval to warn the user prior to the session expiring (20 * 0.9 = 18 mins, so the warning will become 2 minutes prior to the session expiring). – Ciaran Gallagher Dec 06 '13 at 15:06
  • @Stefan I'm aware that ASP.NET will process that before it hits the page... it's just a confusing way to do it, and there are much easier ways to get a server variable to javascript. – MikeSmithDev Dec 06 '13 at 15:07
  • @Mike Yes, I agree this isn't the most elegant way. – Stefan Dec 06 '13 at 15:09
  • @Stefan my hidden point was that (IMHO) its better to have code that is easily understood when looking at it... so I worded my comment from the perspective of what a front-end dev would probably think looking at that code ;) – MikeSmithDev Dec 06 '13 at 15:11
  • @Stefan, what would you suggest as a better way to get the server variable? (I'm not an expert with ASP.NET web forms). – Ciaran Gallagher Dec 06 '13 at 15:12
  • @Ciaran: See this question: http://stackoverflow.com/questions/4396804/access-server-side-variable-on-client-side-and-vice-versa-asp-net-and-javascript – Stefan Dec 06 '13 at 15:14
  • @Stefan, perhaps I should have mentioned this initially, but this doesn't work on Master pages. It only works if you wrap the server variable in the ContentPlaceholder tags (that's why it looks so confusing). – Ciaran Gallagher Dec 06 '13 at 15:20
  • 1
    Another alternative is using a literal: and setting its value in the codebehind. – Stefan Dec 06 '13 at 15:24
  • Like this? "<%= Session.Timeout %>" – Ciaran Gallagher Dec 06 '13 at 15:28
  • Setting it like above caused a compiler error: "does not allow child controls" – Ciaran Gallagher Dec 06 '13 at 16:01
  • Why can't simply var timeoutMins = "<%= Session.Timeout %>"; be used?? – ZedBee Dec 06 '13 at 18:05
  • Apparently, this isn't allowed on master pages @ZedBee, it has to be wrapped in the placeholder tags. Without the tags, I would get a runtime error. – Ciaran Gallagher Dec 06 '13 at 21:54
  • @CiaranGallagher I hope I am not missing something .. but seems to be working perfectly well. Even the session renewal is ok. – ZedBee Dec 07 '13 at 07:16
  • I'll check to see if there's anything else that might affect the session renewal... – Ciaran Gallagher Dec 07 '13 at 09:48
  • @MikeSmithDev, I see what you mean now with the maths. I actually meant to put the calculation of 'remainingTimeMins' BEFORE calculating the 'timeoutMins', as it doesn't make sense to get 10% of the value of 90% of the session timeout value. It doesn't affect the problem though as the 'remainingTimeMins' is only used to display to the user the amount of time remaining in the session (rounded up to the nearest minute). – Ciaran Gallagher Dec 16 '13 at 15:41

3 Answers3

1

I wonder if an AJAX GET isn't enough. It may be that the GET doesn't trigger the whole Page life cycle and so the session isn't renewed. Have you tried to reload the page instead of just issuing a GET? I know it's not as elegant, but would be interesting to know.

Stefan
  • 1,719
  • 2
  • 15
  • 27
  • Reloading the current page would definitely work, but I can't do that as the user would lose the changes they are making. I'd seen the idea of the GET request suggested to me by another StackOverflow user, but perhaps they didn't test their solution. – Ciaran Gallagher Dec 06 '13 at 15:16
  • Yeah, I was thinking that they would lose their changes... Have you looked at the other answer about session ID? Another thing to try is to make sure that the AJAX requests aren't cached with "$.ajaxSetup( {cache:false} )". Finally, this seems to be a good question related to your problem: http://stackoverflow.com/questions/9374190/how-to-reset-asp-net-sessions-state-timeout-with-ajax-request – Stefan Dec 06 '13 at 15:22
  • Turning off caching didn't solve the problem. Checking the related question now... – Ciaran Gallagher Dec 06 '13 at 15:36
1

We found a solution that does not require a refresh of the page the user is on.

This was achieved using a hidden HTML iframe on the page:

<iframe id="keepAlive" src="KeepAlive.aspx" frameborder="0" width="0" height="0" runat="server"></iframe>

I added the javascript methods that implement the timer and dialog to the iframe page. When the user wants to renew the session, they press 'Continue' on the dialog button, then we simply refresh the iframe page. The refresh causes the server to renew the session, without the user losing on the page they are working on - because it's the iframe that refreshes, not the actual page the user is on.

Remember that all this code sits within the ASP.NET Master page, so it is inherited on every ASPX page in our application. The Javascript on iFrame is invoked in the body onload event.

        var intervalID;

        /* Set a timeout interval based on the server timeout value (-10%) */
        function setTimeoutInterval() 
        {
            /* Session timeout warning dialog */

            // Get session timeout value
            var timeoutMins = "<%= Session.Timeout %>";

            // Subtract 10% of the timeout value - to give the user a chance to continue the session before it expires
            var remainingTimeMins = Math.ceil(timeoutMins * 0.1);
            var timeoutMins = timeoutMins * 0.9;

            // Convert the timeout value to milliseconds
            var timeout = timeoutMins * 60 * 1000;

            // Set javascript timeout
            intervalID = window.setInterval("displayTimeoutDialog(" + remainingTimeMins + ")", timeout);
        }

        /* Display a dialog prompting the user to continue the current session or to end the session */
        function displayTimeoutDialog(remainingTimeMins) 
        {
            var result = confirm("The session will end in ~" + remainingTimeMins +
            " minute(s). Press OK to renew, or Cancel to let your session expire.");

            if (result == true) {
                // Keep the session alive and clear the interval
                alert("Session renewed.");
                window.location.href("KeepAlive.aspx");
            }
            else {
                // Redirect to the logout page
                window.clearInterval(intervalID);
            }

        }
Ciaran Gallagher
  • 3,895
  • 9
  • 53
  • 97
0

Ensure that your request contains the same session ID and that you are not using Cookieless SessionIDs.

see http://msdn.microsoft.com/en-us/library/ms178581%28v=vs.90%29.aspx

Session Identifiers

Sessions are identified by a unique identifier that can be read by using the SessionID property. When session state is enabled for an ASP.NET application, each request for a page in the application is examined for a SessionID value sent from the browser. If no SessionID value is supplied, ASP.NET starts a new session and the SessionID value for that session is sent to the browser with the response.

By default, SessionID values are stored in a cookie. However, you can also configure the application to store SessionID values in the URL for a "cookieless" session.

A session is considered active as long as requests continue to be made with the same SessionID value. If the time between requests for a particular session exceeds the specified time-out value in minutes, the session is considered expired. Requests made with an expired SessionID value result in a new session.

  • From the web.config file: cookieless="false". How could I ensure the request contains the same session ID? Why would the session ID change whilst the session is active? – Ciaran Gallagher Dec 06 '13 at 15:23
  • My guess was that the AJAX get is not sending the session id in the request. You can look at the incoming request on the server side page, the Page_load event of KeepAlive.aspx and see if the Page.Request.Cookies collection has the **ASP.NET_SessionId** in it. If it doesn't have the right id then you will get a new session. – user2174334 Dec 06 '13 at 16:13