View source for Your Session Has Timed Out

{{toc numerate=1}}

save failed posts for reuse after forced logout or autologin

<[I wish more developers would test their web applications for session timeout issues. Despite all rumors to the contrary, your users will not be dedicating their entire lives to using your web application in a punctual and timely manner. They have phone calls to take, meetings to go to, other websites and applications to attend to. ---"" ""--- Is it really fair to kick users all the way out of your web application, or worse, blindly reject data they've submitted -- just because they were impudent enough to wait a few hours since their last supplication to the web server gods? In most web apps, the penance is awfully severe for such a common sin.
---"" ""---
-- ((https://blog.codinghorror.com/your-session-has-timed-out/ Your Session Has Timed Out - Coding Horror)) ]>

Forum:
  1. ((/Forum/Discussion/SessionDurationProblem Session duration problem))

{{tree}}

===Settings for Session===
((source:master/src/class/session.php class/session.php))

There are a few key ini settings: 

##session.gc_maxlifetime## -This setting (in seconds) tells the PHP garbage collector how long to keep the session valid.  The default is 24 minutes.

##session.gc_probability## – The probability that the garbage collector will run and clean up old session data.  The default value is ##2##.


##session.cookie_lifetime## – How long to keep the cookie written to the client machine valid.  Defaults to ##0##, which means the cookie will expire at the end of the broswer session (at logout or when closing the broswer).

((source:master/src/class/session.php class/sessionfilestore.php))

##session.save_path## – The path for session values to be saved.  The default is ##/tmp##, however it is important to change this to a custom folder for the application – especially if you are in a shared hosting enviorment.  The garbage collector does not discriminate, and it will delete ANY session data that is older than the set limit, not just ones that correspond to your application.

((source:master/src/class/session.php class/sessiondbalstore.php))

====Custom timeout====

* FACTOR (8*60*60)

class/session.php
%%
public $cf_nonce_lifetime	= 7200*FACTOR;
public $cf_gc_maxlifetime	= 1440*FACTOR;
public $cf_max_idle		= 1440*FACTOR;
public $cf_max_session		= 7200*FACTOR;	// time to unconditionally destroy active session

public $cf_regen_time		= 500;		// seconds between forced id regen
public $cf_cache_expire		= 180*60;	// ttl for cached session pages in seconds
%%

Setting the above configuration well make sure your session will expire in X hours, and the garbage collector will run everytime session_start is called to cleanup expired sessions.

TODO: need config'ing -> constants.php?

====Custom Session Storage====

===Dealing with POST data and expired sessions===
====AJAX====
  * provide the user with the ability to re-login if the session has timed out prior to submitting a POST request.

====JS====
  * Create a background JavaScript process in the browser that sends regular heartbeats to the server. Regenerate a new cookie with timed expiration, say, every 5 or 10 minutes. 
  * Create a warning message show timer with a warning message: "You've been logged off due to long inactivity. Save your form edits to text file. Log in again and re-submit."

That message could be called by function like: 
%%(hl javascript)
setTimeout("session_expiry_warning()", session_duration); // here session_duration is a variable of session duration in milliseconds
%%

The whole messaging system could look like this:
%%(hl javascript)
var session_duration=600000;

function reset_timer(){
	setTimeout("session_expiry_warning()", session_duration);
}

function session_expiry_warning(){
	alert("You've been logged off due to long inactivity.\nSave your form edits to text file to prevent loss.\nLog in again and re-submit.");
}
%%

and this could be also connected with pressing Submit button with:

%%(hl javascript)
if (element.addEventListener) {
    element.addEventListener("submit", function(evt) {
        evt.preventDefault();
        window.history.back();
    }, true);
}
else {
    element.attachEvent('onsubmit', function(evt){
        evt.preventDefault();
        window.history.back();
    });
}
%%

====Session====
  1. make sure all your forms post to them-selfs (302 to them-selfs on success, so you don't have the "refresh" confirmation popup)
  2. In case you have an "if (is not logged in) { redirect to login page; }" block in more than one place, put that in a function and call that function everywhere.
  3. In that function, before the actual redirect, check 2 things:
    a. If the request was GET, and if so, store the URL in a session variable (redirect to it after login)
    b. If the request was a POST, store the #1 the URL and #2 the posted data in the session. Redirect to that #1 URL after the login, and use the #2 data to pre-populate the form fields on the page.
(Make sure to tie the data to the logged in user id or something)

So, from the user's point of view:
  1. login
  1. go to edit profile
  1. fill in profile
  1. go to a meeting for 45 min (session is 24)
  1. come back, submit the edit profile page
  1. get a "login here" page.
  1. login
  1. get redirected to the submitted page, pre-populated with all the posted data, and some kind of message saying to repost, since the data was not saved cause of session time out.

====localStorage====
Autosave

Auto-save function on the edit handler

This functionality is achieved by applying the localStorage function. localStorage is a mechanism for storing data on the browser side. The difference is that you don't have to make a request each time and you can exchange large amounts of data (5 MB in standard practice).

Since Web Storage, in principle, does not allow storing multiple arrays as is, the JSON function defined in JavaScript 1.8.5
to do the arraying and text stringing.

To save to WebStorage, use setItem.

%%(hl javascript)
// save to localStorage
var foo = {bar:0, hoge: 32, ...
%%

When calling it, use getItem and array it with JSON.parse in the reverse procedure.

%%(hl javascript)
// reading from localStorage
var storage = window.localStorage.getItem('foo_data');  // get the data from the localStorage
var data = JSON.parse(storage);         // put it back in the array
console.log(data)
%%

It's easier to read/write than cookies. To remove the data, we can do the following
%%(hl javascript)
window.localStorage.removeItem('foo_data');
%%

=====Implementation=====

We use it to automatically save the editing screens. To determine if localStorage is available, see Modernizr.localstorage

The data to be saved is stored in localStorage.

The localStorage reference name is the page name for the data to be saved, and the last save date and time of the text area is saved in addition to the text data. This date and time is used to determine when the page was last saved. For example, if the last save date and time of a page is newer than the save date and time stored in the localStorage, it can be determined that a page has been modified. So, it also checks if the data stored on the server matches the data on the server. (At best, it will only change the message displayed...)



The data is updated at every onkeypress event. That is, the data is updated at the time of a keystroke. (I'm not sure if this is a good implementation.)

%%(hl javascript)
$(prefix+'.edit_form textarea[name=msg]').keypress(function(){
    // save the contents of the text area to localStrage with the page name
    if (Modernizr.localstorage){
        var d=new Date();
        window.localStorage.setItem(PAGE,JSON.stringify({
            msg : $(this).val(),
            modified: d.getTime()
%%

Processing at loading:

%%(hl javascript)
// get the contents of the text area from localStorage
if (Modernizr.localstorage){
    var msg = $(prefix+'.edit_form textarea[name=msg]').val();
    var storage = window.localStorage.getItem(PAGE);
    var data = JSON.parse(storage);

    if (data){
        // There's a fatal problem: if you update with "Don't update timestamp", there's no way to detect it.
        var ask = (MODIFIED > data.modified && data.msg !== msg) ? 
            'The page seems to be newer than the data I edited in the past. Do you want to restore it?' : 
            'It looks like I have some data that I have edited in the past. Would you like to restore it?';

        // restore the data
        if (confirm(ask)){ $(prefix+'.edit_form textarea[name=msg]').val(data.msg);
%%

The timing for deleting the data in the localStorage is when the update is made or when the cancel button is pressed. However, in the current implementation, we don't have confirmation that the data is stored on the server, so it's not enough yet.

%%(hl javascript)
// What happens when the send button is pressed?
$(prefix+'form').submit(function(){
    // flush localStorage (even if you press the cancel button)
    if (Modernizr.localstorage){
        localStorage.removeItem(PAGE);
%%

This is the purpose for which it is used.

  1. ((https://github.com/simsalabim/sisyphus/ Sisyphus.js))


===Tools===
  1. ((https://addons.mozilla.org/de/firefox/addon/form-history-control/ Form History Control ))
  2. Lazarus