Skip to main content

How to build the Perfect Login System

It's been a while since I've been asking the internet this question. While there are many solutions, boilerplates & code snippets available, few explain the reasons behind putting things the way they are.

closeup photo of turned-on blue and white laptop computer
Pic by Philipp Katzenberger
This Login System will be intended for a Website, but if you do get the gist, you can apply the same to other platforms too.

Where do we need a Login System?

Almost any project with a User model. Protecting and segregating User data is of prime importance, and with that, we also need to add validator middlewares for other routes.
Validator Middlewares restrict access to simple routes such as Dashboard, Messaging, or any other service route which you do not want any non-logged in user to access.

What do we need?

This is an important question. Okay, you've specified what the problem is and what do you want. But now you've to realize what you really want. Visualize:
You've built a website. The user has signed up, then the user has logged in. Each time a user does this, the server shall receive a request to validate the email and password, child's play. Your concern is what happens after.
The user wants to go to the dashboard or the profile page (whatever you've chosen for your project), but the backend doesn't know if the user is logged in or not. We need to send something to the server for it to realize that this is a Logged In user. Then the server can fetch their data and return with the required information.

What can I send?

Good question. Before giving something, ask yourself what do you have. As soon as you log in, if the email and password match, the server sends you a 200 response which means that yes, the user is logged in. Right at this moment, the server will send a unique key, provided only to that particular user (although the user doesn't have to know this). This will all happen behind the scenes in the browser code. 
You shall decide what that unique key is. If you're using MongoDB, it could be the _id of the user's document. 

Climax 1:

And when the 200 response from the server accompanies a unique key, we save that unique key to the Local Storage of the browser, which we can accomplish by adding the following in the JS file:

LocalStorage.setItem()

You can google more about this function. Now whenever you have to make a request, get that 'item' from Local Storage and send it along with the request. The server gets the unique key, identifies the user and it can process whatever it wants through it.

Problem:

Yes, the idea seems really easy and flawless. But let's cross-examine the flow with all possible cases. The user logs in, and the userId is forever stored in your browser's Local Storage. This will also keep the user Logged In forever (like the big professional sites). However, there's one corner case:
If someone else copies the unique key from the Local Storage of the logged in user's browser, and pastes it into another PC, they might log in too (in a way hack the user's account).
 In such a case, there's nothing a user can do to change it ever. The hacker can always remain logged in through the account. Because the server has no record of keeping the user's data, we will have to change the unique key when the user asks us to, and seeing that we have used the userId of the record, we will have to delete the entire user data from the DB!

Climax 2 and Final Solution:

We need to have a record of log In with the server for safe logins and also need to change the unique key. Clearly UserId is too precious to be given out in the open. Here are the tips:
  1. For the unique key, send out an encrypted token containing the userId and the time of login.
  2. Since we will also need to decrypt the token, choose a wise method of encoding. Don't worry about it being decrypted by other hackers, since even if they have the userId and time of login, they still won't be able to login.
  3. On the server side, you need to check for a time gap every time you log in. If the user's time of log in and current time is, say 10 hours, then the request must automatically be denied and redirected to the Log In page, basically, log out the user.
  4. In the Change Password option, provide a new token to the real user, with the userId and current Time encoded. (Preferrable encoding option: JWT)

Final Cross Examination:

Okay, let's check how strong is our login system.
If someone does get access to the encrypted token too, they won't be logged in permanently even if the real user doesn't know about it. Remember, the hackers will never know the password of the user since its safe in the DB and there's no route giving that away. If the user does realize their account is hacked by finding out some suspicious activity, they can change the password and the hacker is automatically logged out then.

Note: You can still add an extra check on the user's security, just in case if the user doesn't realize that their account is hacked. In the Database, add another field in the User's document, storing the initial IP and location of the user at the time of sign up. This way, if the IP changes, that is, a new log in from another PC takes place, you can send a mail to the User asking their permission. 

For this, you will need to add a mailing functionality in your login endpoint. Just like all the safe professional websites! 
  
P.S.: I'm sorry I didn't write any code, but this part was mainly a discussion about the Login System. Stay tuned for the boilerplate code of this!  



Comments

Post a Comment

Popular posts from this blog

Namaste JavaScript Quick Notes

Note:  Akshay Saini's Namaste JavaScript is probably the best course for JavaScript developers out there. These are my personal notes that I made while watching the course; they serve more of as an online quick reference for my understanding and revision, and I hope it benefits anyone reading it too! Everything in JS happens inside an Execution Context. Before a JS code is run, memory is allocated and variables are set as undefined   , and functions are set as their exact code in the scope within the Execution Context. The global execution context hosts all the global variables and function definitions. An Execution Context has 2 components: Memory, that stores variables and functions; and Code, that reads and executes the code. Call Stack maintains the order of execution contexts. Since JS is single threaded and asynchronous, at one point of time, only one function is executed which is at the top of the call stack. For each function, an execution context is created before executi

i3wm essentials - I (Brightness)

So you have started using i3 and somehow managed to open your browser and almost resumed your normal work.  But wait, the brightness is too much isn't it? Or is it too low? The mousepad used to work fine, but now all of a sudden tapping does not equal click?!  Don't worry.  This blog series will tell you all about the essential setup commands and common shortcuts that I use to navigate my work in i3, and how you can too. Changing the brightness So you just started i3 and you just can't take this brightness setting. You go for your function keys, and damn! They aren't working. Quick fix: Run the following command if you need to change the brightness ASAP. xrandr -q | grep ' connected' | head -n 1 | cut -d ' ' -f1 This will give an ouput that's the name of your monitor.  Use that monitor name here and change the values of brightness to suit your needs. xrandr --output <monitor-name> --brightness 0.7 Now that your eyes are comfortable, let me show

i3wm essentials - II

Welcome back! Let's continue this guide with other setup essentials for i3. Enabling Mousetap Chances are that if you're using a laptop, then tapping on the mousepad does not equal a click for you. You need to enable tapping in your config. Fortunately, there is one documentation available that works for majority of the setups. I don't need to explain this one in detail. Here you go: Enable tap to click in i3 . Volume Control This one is simple again. Do you remember the i3 config file I talked about in the previous blog ? All you need to do is go to that file and find the line: bindsym XF86AudioRaiseVolume Just below that line you will find lines with XF86AudioLowerVolume and XF86AudioMute too. Anyway, the truth is, there are 2 sets of lines with these keywords. Chances are that the line: bindsym XF86AudioRaiseVolume exec --no-startup-id pactl -- set-sink-volume 0 +5% Will be uncommented and the line: bindsym XF86AudioRaiseVolume exec --no-startup-id pactl -- set-sink vo