Beware this content is over 6 years old and considered stale. It may no longer be accurate and/or reflect the understanding of the author but remains here for reference only. Please keep this in mind as you use this content.

A new project (that @willvrny was working on) which incorporates a user logging process and a custom user area brought with it a few new challenges.

One particular one was the scenario that if a client which buys a certain number of logins to the service then begins to share that single login around it’s offices thereby allowing a whole office to use the service through a single login, instead of purchasing a unique login for each user.

Therefore, we needed a way to manage a users login session by keep track of the number of concurrent connections or logins, which would be one session per a login.

While the traditional/easy way is to store a users session in the database at login and then each time the page loads to match up the user stored session with the current users session and if they do not match redirect the user to the login page with an appropriate error message.

The problem with the service that we were providing is that it would be possible for multiple users to consume the service while remaining on the same page.

So one user could login, navigate to a resource and then allow another user to login and navigate to another resource they needed, so that both users would be consuming different resources on different sessions using the same login.

That’s where the AJAX magic comes in. The theory is that you should be able to poll the database for the current session at regular intervals, asynchronously, and detect whether or not the stored session and the current session match up. If they do, do nothing, if they don’t eject that user and kill their session.

The following was what we were able to come up with and I would welcome feedback or alternative solutions.

On the server-side: PHP

I assume you already have a working knowledge of PHP sessions, particularly how to start them, set session variables and retrieve/get session variables. You’ll need to know how to do this in order to store the automatically generated session_id in the database after a successful authentication/login and to retrieve it for your scripts.

Things to note is that while I simply unset the PHP session variable (for demonstrative simplicity) using: unset($_SESSION). You will probably want to utilise your own function to kill the session altogether.

I also have left a function get_session_reference($user_id) in there that retrieves the stored session_id from the database taking the users id as an argument. There is also a custom function get_user_id() that retrieves the current users id which will be the current authenticated user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// POST value.
if (isset($_POST['session_reference'])) {
// check for match.
if ($_POST['session_reference'] != get_session_reference(get_user_id())) {
// kill the session.
unset($_SESSION);
// send error response code.
header('HTTP/1.0 409 Conflict');
}
} else {
// kill the session.
unset($_SESSION);
// send response code.
header('HTTP/1.0 401 Unauthorized');
}

On the server-side: HTTP Status Codes

While it becomes more obvious why the script is returning/setting HTTP status codes using the header() function when you view the JavaScript, let me explain.

The first few attempts used a straight header() location redirect e.g. header('Location: https://www.example.org/logged-out/') But it was discovered that the way the jQuery AJAX function works is by including the server-side output into the main HTML document from which it is getting called. This prevents a proper redirect because for the redirect to work it needs to occur before any output is sent to the screen.

This solution utilises the callbacks for the jQuery AJAX function that allow it to handle response codes, based on success or failure.

The idea is that if we send error status codes back to the AJAX function we can perform redirects on the page making the requests using a JavaScript location redirect.

While I appreciate it may not confirm to strictly correct usage for the HTTP status codes, it allows us to send different error status codes depending on the outcome of the script. One outcome for those that send no session reference to the script and another for those that send an invalid session reference.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function validate_session() {
$.ajax({
type: 'POST',
url: 'https://www.example.org/ajax/check_session/',
data: 'session_reference=123456',
success: function(data, textStatus, XMLHttpRequest){
//$('#session-output').html(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
//$('#session-output').html(XMLHttpRequest.status);
// if no session -> unauthorised.
if(XMLHttpRequest.status == '401')
$(window.location).attr('href', 'https://www.example.org/?error=session_not_found');
// if session expired -> conflict.
if(XMLHttpRequest.status == '409')
$(window.location).attr('href', 'https://www.example.org/?error=session_expired');
}
});
};
setInterval('validate_session()',2000);

On the client-side: jQuery AJAX

While the JavaScript should be self-explanatory it would be worth pointing out some of the features.

On the AJAX error callback the XMLHttpRequest (XHR) object is used to retrieve the status code that we sent from the PHP script which compares the sent/current session with the one that is stored in the database for that user.

That way a different redirect is used depending on the status code.

The whole AJAX function is wrapped up as a function so that it can be repeatedly called at regular intervals using the setInterval() JavaScript function.

That’s pretty much it. Please shout if you need more clarification or if you have a better, more elegant solution to this problem.