1

My one time reset password link is like this with a token http://url.com/token. The payload of the token contains a password (hashed)

When the user request forgot password, then this token is generated like this

jwt.sign({ password, email }, secret, {expiresIn: "1d"})

When the user clicks the link and fills out the reset form. The password reset request is sent to the server and in the body contains the token from the url and the new password.

jwt.verify(req.body.token, secret)

Then I just checked if the hashed password in the database matches the one shown in the jwt payload. If it does, I will change the password in the database. If it doesnt,the one time link has been used already so I error (returns 400 error).

How to do this without storing password?

user349557
  • 83
  • 1
  • 12

2 Answers2

1

I would definitely refrain from sending the old password hash in the JWT. If someone were to get access to a bunch of these, they could use them to try to brute-force hack your authentication.

What I would do is have a separate table for password reset requests. So every time someone asks for a reset link

  1. You create a new password reset request entry in your DB with the reset token
  2. If there already was an existing password reset request for that user in your DB, you delete it
  3. The user submits the reset request with their new password and the token
  4. You query your password reset request table and verify that the token they provided matches the token in your DB (i.e. because you only keep the most up-to-date one, you know that it is the correct one)
  5. You action the password change and delete the password reset request from the DB

Good luck!

Pelayo Martinez
  • 287
  • 1
  • 8
  • 1
    2 means that someone could effectively deny your users from resetting passwords. A malicious requestor who continually sends fake reset requests that delete previous requests is all that is needed. Just don't delete, rather store requests with time stamps and make them expired automatically if not used – Wiktor Zychla Jul 12 '22 at 18:12
  • Ahh very good point I missed! Thank you @WiktorZychla! – Pelayo Martinez Jul 12 '22 at 18:15
  • 1 would be the easiest to implement for me. What should go into: "new password reset request entry" column in the users table? I think maybe a uuidv4() and then upon password change I reset it? I think true or false could work too. – user349557 Jul 12 '22 at 19:18
1

People can try to crack a password hash even if it takes a long time: https://security.stackexchange.com/questions/199494/how-to-crack-a-password-given-its-hash-and-its-possibilities

This makes your password hash sensitive which means you should not put it in your JWT token. https://stackoverflow.com/questions/43496821/is-it-safe-to-store-sensitive-data-in-jwt-payload#:~:text=Ideally%20you%20should%20not%20store,by%20simply%20base64%20decoding%20it.

The time complexity/security risk depends on a lot of things(be sure you are salting your password).

This is a common problem with JWT tokens: Here is a link to some solutions: Invalidating JSON Web Tokens

One that was missed would be adding a password version(some number that increments when the password is changed) to your password. Then passing this version back in your token, instead of the password hash. Since you are already querying your database(to make sure the password is the same) you can just query to make sure the password version is the most recent version without any extra time complexity. If it is the same change the password. If it is not the same do not change the password.

brandon s
  • 71
  • 5