How I Set Limit for User Concurrent Sessions on Tradepally.com
Simple way to control the number of browsers/devices a user of a web app can be active on, at a time, which lets user set his/her own verification OTP
Intro
I happen to be very security conscious with everything I do, the same way I don't allow anything with my name on it to come short of my best effort at any given time. Sometimes, it delays my work but in the end, I always appreciate myself more for the better results I get from the extra effort.
One such extra effort in recent times was adding a custom User Concurrent Sessions Limiter—which controls the number of browsers/devices users can be active on, at a time, and also allows them to set their login verification OTP by themselves—to my bookkeeping platform for trading communities, Tradepally.com.
In this article, I'll be explaining why, and how I did it, so that others who are looking for ways to do the same can tap in and replicate. And, trust me, it's very simple.
While you can jump to the "How The Feature Was Built" section of this article right away using the table of contents, I'll advise you to read through the entire piece to understand the thought behind the solution. It's just 7 minutes, minus 1 that you've already covered. Either way, let's get it on.
Why The Feature
There can be a variety of reasons why this feature would be required in a web app. For me, that reason is to minimise the possibility of unauthorised access to a user account on Tradepally via the browser. This becomes a necessity due to how events flow on the platform. Using the hierarchy of sessions on Tradepally—which goes in the way of USER Session -> BUSINESS Session -> BUSINESS OUTLET Session—let me clarify with the following details:
A Tradepally User Session lasts 30 days before expiring. This ensures that the most reluctant user does not require fresh login with email and password each time s/he uses the platform, even if s/he records only total income and expenses once every month. But with that comes an obvious security issue: anyone who picks the logged-in device can view/edit/delete some user-specific data if the user doesn't have the Optional User Validation Pin turned on.
A Tradepally Business Session lasts 30 minutes (by default) before expiring. This seeks to compensate for the very big user session window. So, even if a random person picks up a logged-in device—except within 30 minutes of the last business operation—they won't have access to any important business data unless they have the Compulsory Business Access Pin which is required to unlock the business profile after every 30 minutes of inactivity and for all create operations.
A Tradepally Business Outlet Session is tied to that of the Business that owns it. But, it doesn't mean that anyone with access to a business profile automatically has access to the business's outlets' operations and data. To gain access to all that, explicit permissions will have to be given by the business account owner.
From the details above, it's clear that the user profile—which is the base gateway to every other private good on Tradepally—has the biggest security issues, namely: 30 days session timeout period and Optional validation pin, yet, security anywhere, we understand is as strong as the weakest link. Hence, the need to put measures in place to watch the base gateway: user profile.
How The Feature Works
Having outlined the issues that led to adding this feature, I'll now explain how it works, before going on to how it was built.
The image above shows the response received each time a registered user tries to log into Tradepally from a second or third browser/device while already active in at least one other browser/device. In essence, the solution is simply to stop any further login after the first, except a system-generated OTP (sent to the user's email and push notifications) is provided, or —the twist— the user can set his/her own OTP via text message, from the phone number associated with their account. The image below shows how the latter works (emphasis on the area surrounded with a red line):
This double-edged sword OTP approach—for multi-logins and other verifications on Tradepally—has saved the day many times for a few users. The idea is simple: if the user fails to receive the system's OTP, then the system should receive the user's OTP. Either way, OTP must be sent, received and used, on time. NOTE: This approach charges the user for the SMS sent, but that is not a problem for most users in the situation.
Lastly on this, once a user reaches the maximum number of active logins allowed (3, currently), the system will start to halt any new login attempt—with or without OTP—and display the message seen in the image below:
As you can also see from the message in the image, the feature also provides a very simple way for the user to reset their active sessions with just a text message from their registered phone number in case they lose access to all 3 logged-in browsers/devices.
That, in a nutshell, is how the solution works. It leads us, finally, to how it was built: with love for the users.
How The Feature Was Built
The base of this solution is simply a Column
on the User
table, or on a dedicated separate table
, holding a JSON field and named in the lines of userSessions, userActiveSessions etc. A column on the User table will be easier to implement at first, but a dedicated separate table—with a one-to-one relationship to the User—is much more robust and future-proof, in case more supporting columns become expedient. The json column holds a collection (list/array, dict/object, set etc)
of objects, each representing a custom user session
. The length of the collection counts as the number of active sessions. A new object gets added to the collection each time the user logs in afresh, is updated as the user interacts with the system, and then is deleted when the user logs out or resets active sessions via an inbuilt Reset Active Sessions function.
The most important contents of each object in the collection relevant to this article include UserId, UserAgent, LastActivityDatetime and CustomSessionId. The UserId
identifies the user in the session, the UserAgent
stores info (as generated by whichever web framework of choice) about the browser/device being used, the LastActivityDatetime
keeps track of the last time the user interacted with the system and enables custom timeouts while the CustomSessionId
(which, once generated -as any ID type of choice, is also saved in the browser's session object) ties the custom session to the browser/device through it being present in both the custom and browser sessions' objects.
Simply put, once a new login is verified, all the items mentioned in the preceding paragraph get generated and put in an object as key/value
pairs, and then the object is added to the custom session objects' container, i.e the collection talked about in the first paragraph of this section. The CustomSessionId
generated is then also added to the browser's session object and that becomes the link between the user's custom session object and that particular browser/device. And, hurray, that is it. You now have your core custom session in place and can do whatever you wish with it, including updates, checking for the number of active sessions (to know when to halt or allow new logins) and deletions, etc.
Now, about the verification OTP part, all that is required for the system generated—i.e system to the user—OTP is integration with any outbound SMS API service provider of your choice. For that I use BetaSMS, but they all have their docs on how to use them. And for the user-generated—i.e user to the system—OTP, a different type of set-up and work (different from that of the regular outbound SMS) is required. I think that is outside the scope of this article, so, I'll have to write a separate piece for it, to be able to explain it very well. Meanwhile, I used Twilio's inbound SMS service to implement it.
That's it for now on how the solution was built. I may have left out some delicate details due to safety because Tradepally.com is still in active service. But I believe that I've shared enough for many, who wish, to understand and replicate the solution.
Conclusion
As I said in the beginning, I'm very security conscious with everything I do. But, that doesn't mean that I'm a security expert. Instead, it means that I give my best shot at it based on the much I know at every given time. So, if you're looking for a way to control the number of browsers/devices users of your web app can be active on, at a time; the info I shared in the last two sections should get you through.
And, if you're a web security expert and/or have found any security loopholes in my implementation, please help me point it out in the comments here or, if you so wish, in private. I'm on Twitter @tradersTechie.
Also, if you found something interesting or have any questions, help me mention it, it'll help me understand better; what I'm doing right or wrong. Together, let's keep the web and the private contents it holds, safer.
Thanks for reading.
The Traders Techie