Database-less Password Resets using JWT
Fully functional forget password / password reset functionality without storing any tokens in the database.

I was given a task to setup Authentication for our application and I had a few unsaid restrictions, it was not sensible to modify the database schema if not absolutely necessary because it is a complex database with many connected tables which are coupled tightly.
I was required to create a mechanism for users to reset their passwords using their email ids. Generally while doing this, we generate a random string and store in the database as a token for password reset, which we will verify while performing the reset.
But here I was required to generate password reset links without modifying the schema of the database. Which made me come up with a rather unconventional solution, although non-standard, but this method is 100% secure and reliable to use in production. Let’s see how it works.
When a user submits a password reset request using their username or email, we need to fetch the user’s details on the server side. If the user with the provided username or email exists, we proceed further or we return an error.

If the user exists, we get their current password, it does not matter if the password is hashed or not. We will use this current password string as one of the parameters to create a JWT or a Javascript Web Token.
So, if you are not aware of it, a JWT or a Javascript Web Token is a token which can be used for authentication and authorization. The contents of this token can be read by anyone who has access to it, but it can be verified / checked only by the creator of the token, because the creator holds to secret key for the token.
So basically, we need a secret-key to create it which we will use while verifying the token. Normally, we have a secret-key in our .env file which is used to create all the tokens which are spit out by our backend.

You might be wondering why do we have to use the CURRENTPASSWORD + SECRETKEY combination to generate the token instead of just the secret key. And the real cleverness of this approach stays in this simple thing.
If we generate the token just using the secret key, we will be able to use it infinitely till it’s expiry date. Which is harmful for us as it will be a loophole to abuse the reset password system and poses a huge security threat. By using the combination of current password and secret key, we are making it a one time use token. Because once the password is updated in the database, we can no longer verify the token, because it was generated with the old password. Even if the user set’s the same password again, it won’t be verifiable until and unless we are not hashing the passwords, which is not recommended at all.
The jwt tokens also have an expiry time which we set at the time of generation, after which the token does not get verified. So we don’t have to implement that separately.
I am writing some pseudo code in javascript on it’s implementation
const generateToken = (username) => {
const user = db.getUserByUsername(username);
const claims = {
"username": user.username
}
const secret = `${user.password}${process.env.SECRETKEY}`;
const token = jwt.(claims, secret);
return token.compact();
}
const verifyToken = (token) => {
let verified = true;
const tokenBody = jwt.getBody(token);
const user = db.getUserByUsername(tokenBody.username);
const seceret = `${user.password}${process.env.SECRETKEY}`;
try{
jwt.verify(token, secret);
}catch(e){
verified = false;
}
return verified;
}
Please note that the above code is pseudo code and the actual implementation may have different syntax.
I am personally using the njwt library in nextjs for this. Different languages can have different libraries, but the logic stays the same!
Thank you for reading.


