So I came across this issue and couldn't figure out what was causing it. I have implemented an authentication flow using JWT with access- and refresh tokens. I made the refresh tokens expire after a long period of time, and they can be forcefully reset to prevent stolen refresh tokens from being used again.
To do this, I hash the jwt refresh token using bcrypt and store it in my database. However I always get a true result when comparing the raw jwt to the hash and I'm not sure why.
I searched for some bit and I think this is happening because bcrypt doesn't like the 191 char tokens, and trims them to a max length, and since the first part of the jwt is similar, I get a valid result when comparing the hash. Is this true? If so, how can I extend the max length to fit my tokens or should I pre-hash the tokens before passing them to the original hash function?
await bcrypt.compare('loooooooooooooooooooooooooooooooooooooooooooooooooooooooong', hash);
// true
await bcrypt.compare('loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong', hash);
// also true
Any help is appreciated :)
Edit:
Okay so I've been thinking about this for a bit and think I've come up with an okay solution.
When signing the refresh token, I also generate a random password and store it in the payload. Instead of storing the hash of the jwt itself, I store the hash of this password in my database. So whenever I want to verify the refresh token hasn't been blocked, I first verify the token itself, and then verify the password in the payload to my stored hash. If the refresh token has already been updated the password in the payload will no longer match the hash from the newly signed refreh token, thus making it invalid.
Opinions from future readers?