Thursday, January 13, 2011

Session Timeout with Warning and jQuery Session Refresh in ASP.Net

ASP.Net applications are written in such a way that after the session times out, the user is also logged out. This is sometimes to secure the application from others accessing the computer, while the real user is away from their desk. If this is the case, it’s nice to let the user know how long they’ve got left before they’re logged out due to inactivity. I got this from This Site Written in Cold Fusion.
I Converted To work in C# and Added Progress Bar With in Message Box.



Download

Summary

- User logs in, session created.
- Session set to time out (e.g. 30 minutes).
- When session times out, the user is logged out.
- Display a countdown so the user knows how long is left.
- Inform the user when they are approaching the limit (e.g. 5 minutes left).
- Let the user know that they’ve been automatically timed out due to inactivity.

Approach taken

Every request to the application will set two cookies:
Date / time the session will expire.
Current server date / time.
Our bit of JavaScript watches these cookies.
Any application activity will update these cookies so we know if a second window has done something and the session timeout refreshed.

sessionTimeout()


This is my first real effort at creating a jQuery plugin. It took a bit more effort wrapping everything up nicely and providing options but I think it was worth it and should make the code easier to include.

   (function($) {
    $.fn.sessionTimeout = function(options) {
        var opts = $.extend({}, $.fn.sessionTimeout.defaults, options);
        var inter = this.data('timer');
        if (inter) {
            clearInterval(inter);
        }

        var info = {
            warned: false,
            expired: false
        };
        processCookie(info, opts);

        this.data('timer', setInterval(cookieCheck, opts.interval, this, info, opts));
        cookieCheck(this, info, opts);
    };

    function processCookie(info, opts) {
        info.serverTime = Date.parse($.fn.sessionTimeout.readCookie(opts.timeCookie));
        info.sessionTime = Date.parse($.fn.sessionTimeout.readCookie(opts.sessCookie));
        info.offset = new Date().getTime() - info.serverTime;
        info.expires = info.sessionTime + info.offset;
        info.duration = Math.floor((info.sessionTime - info.serverTime) / 1000);
    };

    // private
    function cookieCheck(els, info, opts) {
        var sessionTime = Date.parse($.fn.sessionTimeout.readCookie(opts.sessCookie));
        if (sessionTime != info.sessionTime) {
            processCookie(info, opts);
        }
        info.timeLeft = {};
        var ms = info.expires - (new Date().getTime());
        info.timeLeft.minutes = Math.floor(ms / 60000);
        info.timeLeft.seconds = Math.floor(ms % 60000 / 1000);
        info.timeLeft.onlySeconds = info.timeLeft.minutes * 60 + info.timeLeft.seconds;
        info.timeLeft.minutes = info.timeLeft.minutes.toString().replace(/^([0-9])$/, '0$1');
        info.timeLeft.seconds = info.timeLeft.seconds.toString().replace(/^([0-9])$/, '0$1');
        if (!info.warned && info.timeLeft.onlySeconds <= opts.warningTime) {
            info.warned = true;
            opts.onWarning(els, info, opts);
        } else if (!info.expired && info.timeLeft.onlySeconds < 0) {
            info.expired = true;
            opts.onExpire(els, info, opts);
        }
        if (!info.expired) {
            opts.onTick(els, info, opts);
        }
    };

    function onTick(els, info, opts) {
        els.each(function() {
            opts.onTickEach(this, info, opts);
        });
    };

    function onTickEach(el, info, opts) {
        var pval = ((info.timeLeft.minutes * 60) + parseInt(info.timeLeft.seconds)) * 100 / opts.warningTime;

        $(el).html(info.timeLeft.minutes + ':' + info.timeLeft.seconds); //+ ' ' + opts.warningTime + ' ' + pval + '  ' + info.duration);
        if (pval < 100) {
            if (!$("#Session-TimeOut").dialog('isOpen'))
                $("#Session-TimeOut").dialog('open');
            $(".bar").progressbar({
                value: pval
            });
        }
        else {
            if ($("#Session-TimeOut").dialog('isOpen'))
                $("#Session-TimeOut").dialog('close');
        }
    };

    function onWarning(el, info, opts) {
        //alert('Warning');
        $("#Session-TimeOut").dialog('open');

    };

    function onExpire(el, info, opts) {
        window.location('Login.aspx');
        //alert('Expired');
    };

    // public
    $.fn.sessionTimeout.readCookie = function(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') c = c.substring(1, c.length);
            if (c.indexOf(nameEQ) == 0)
                return unescape(c.substring(nameEQ.length, c.length));
        }
        return null;
    }

    $.fn.sessionTimeout.defaults = {
        timeCookie: 'SERVERTIME',//cookie 
        sessCookie: 'SESSIONTIMEOUT', //cookie
        interval: 1000,
        onTick: onTick,
        onTickEach: onTickEach,
        warningTime: 340, // seconds
        onWarning: onWarning,
        onExpire: onExpire
    };
})(jQuery);


The plugin provides .sessionTimeout(options) which is used on your selected elements to display the amount of time left until session expiration. The options allow you to use different cookie names, change the execution interval and override several events.
It works by setting up a periodic function to watch the cookies. Whenever the cookies are updated, we recalculate the time out and continue displaying the information. If the warning time or expiration is reached, it fires off over-ridable events that by default use “alert” to display simple messages, but could easily use something like the jQuery UI dialog plugin.
If you’re wondering about the reasoning behind the server time cookie. This was to workaround the differences between the client and server clocks.

Example


In the following code I’ll set the two cookies required for the plugin and use it against two different elements. One for displaying the time, another to show a progress bar (using jQuery UI, not required for the plugin). I also override the onWarning & onExpire events for the progress bar since the user wouldn’t like to be double prompted

Add Jquery And Jquery UI

  <link href="../App_Themes/TestTheme/jquery-ui-1.8.2.custom.css" rel="stylesheet"
        type="text/css" />
    <link href="../App_Themes/TestTheme/stylemain.css" rel="stylesheet" type="text/css" />
    <script src="../JS/jquery-1.4.2.min.js" type="text/javascript"></script>
    <script src="../JS/jquery-ui-1.8.2.custom.min.js" type="text/javascript"></script>
    <script src="../JS/Timeout.js" type="text/javascript"></script>    


Init Session Timeout And a Dialog Box To Dispaly Alert
          $(document).ready(function() {
            $(".sessions").sessionTimeout();
            $("#Session-TimeOut").dialog({
                resizable: true,
                height: 200,
                autoOpen: false,
                modal: true,
                buttons: {
                    Ok: function() {
                        $(this).dialog('close');
                        __doPostBack('<%= Button1.UniqueID %>', '');
                    }
                }
            });
        });   


default.aspx
   
Your session is about to Expire.




add cookie on PageLoad

  HttpCookie appCookie = new HttpCookie("SERVERTIME");
        appCookie.Value = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
        appCookie.Expires = DateTime.Now.AddDays(1);
        appCookie.Path = "/";
        Response.Cookies.Add(appCookie);
        HttpCookie appCookie2 = new HttpCookie("SESSIONTIMEOUT");
        appCookie2.Value = DateTime.Now.AddMinutes(HttpContext.Current.Session.Timeout).ToString("yyyy/MM/dd HH:mm:ss");
        appCookie2.Expires = DateTime.Now.AddDays(1);
        appCookie2.Path = "/";
        Response.Cookies.Add(appCookie2);

Running this bit of JavaScript fires the alert shown below:

Download