This solution is applicable in the context where the application itself is logging and maintaining data of currently
logged in users and multiple instances of the same user having seperate sessions with the application may cause
problems. Record time stamping may be used, but in many case for a single user, blocking multiple logins makes
more sense. An example of such an application would be an online assessment system which allows learners to take
online assessments set up by their training supervisors. Here multiple instances of same user taking the same
assessment at the same time will create serious concurrency problems.
Blocking multiple logins may seem a very simple problem at surface, but it is not. You may set a bit in the database
or maintain a collection of currently logged in users in an Application variable and compare each login request
against the DB bits or values in application variable. This works as long as user uses your logout button or link
to logout from application (on which you reset the bit or remove the username from application variable), but what
if the user's session expires, browser window crashes, machine reboots, or internet connection drops.
In these cases, the user's account will be locked indefinately until unlocked by an admin.
This is not a very elegant solution, but unfortunately many implementations are set up this way.
The challenge here is that we can not possibly add handlers for each and every possible way a user's session can
end. It is the same problem that must be faced by a web server: how to detect that a user is no longer using an
application in order to terminate his/her session to free up resources? The answer is simple, the web server does
not need to worry about the multitude of scenerios in which a user exits - it simply maintains a session timeout
counter, which runs from 0 to the Session.Timeout value set for the application. This counter restarts from 0 on
each page load by the user, allowing him/her another Session.Timeout time. If this counter completes
(i.e. Session.Timeout time elapses since user accessed any page in the application), it implies that user has
finished interacting, and web server can safely terminate the session.
We can develop our own logic based on the same principle used by a webserver to detect session completion.
We can detect the expiration / completion of a session with the use of the same Session.Timeout property.
What we need to do is to maintain in the database the last access datetime per user.
Now for each login request, we just need to perform the following check:
Pseudo Code
LoginValidate() Function
{
If (Current DateTime - User's Last Access DateTime in DB) > Session.Timeout
allow login;
Else
disallow login;
}
Now even if the user's session expires by close of a browser, expiration of session, machine crash, or internet disconnect,
his account locks only until the session expiration time elapses. After that, the subsequent request to login will succeed.
If you implement blocking multiple logins in this manner, you will no longer have to worry about user accounts getting
locked indefinitely – the lock will last only until the session expires or the users logs out.
What do you need to do in terms of implementation to develop this functionality? There are 4 main requirements:
- A datastructure to maintain Last Access DateTime value for each user
- Mechanism to determine whether to allow a login request or not on the basis of comparison between difference of Current DateTime and Last Access DateTime with Session Timeout
- Mechanism to update Last Access DateTime value for currently logged in users on each page load
- Mechanism to reset Last Access DateTime value on logout to allow subsequent login for same user
There is a complete working sample implementation of this block multiple login functionality available for
download at the end of the article.
Refer to the readme.txt file included in the zip file for steps to setup the sample on your machine.
In the sample implementation I am using a table named UserSession to maintain the Last
Access Date Time for each user which satisfys the first requirement listed above.
In order to determine whether or not to allow a login request, I am using a stored procedure named GetSession
which is called from Login ASP page (default.asp). It takes two input parameters (@username and @session_timeout)
and returns one output parameter (@allowlogin) that contains either 0 or 1.
This return value is used by the ASP script to determine whether to allow a login or not.
I am using another stored procedure named UserSessionRefresh in order to update the Last Access DateTime value.
In order to do this, the stored procedure needs to be called on each page load. The sample has only one page
(home.asp) so the call to this procedure is on this page, and a link is provided to reload the page which also
refreshes the session.
For the final requirement of resetting the Last Access DateTime value on logout, I am using a stored procedure
named UserSessionClear which is called from my logout page. A link is provided on home.asp for logout that
redirects you to logout.asp. Additionally if your session expires, and you try to reload the home.asp page it
will automatically redirect you to logout.asp which in turn will clear your session in db, call Session.Abandon
and then redirect back to login screen.
All the common functions called from the individual asp pages can be found in the includes\commmon_function.asp file
also included in the download zip file.