I'm using bcryptjs to hash a user's refresh_token before storing it in my database.
It seems that the following always evaluates to true when comparing a hashed string with a JWT, I've also gotten the same behavior on https://bcrypt-generator.com/
for example the hash $2a$10$z4rwnyg.cVtP2SHt3lYj7.aGeAzonmmzbxqCzi2UW3SQj6famGaqW
is a match with the following two JWTs
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk1OTg2MDIsImV4cCI6MTY0MjE5MDYwMn0.aJlzFHhBMGO4J7vlOudqOrOFnL1P-yEGrREgdaCXlxU
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk2MDY4ODgsImV4cCI6MTY0MjE5ODg4OH0.vo4HKLXuQbT0Yb0j21M4xl-rakxyE5wINjuGdkPuSJY
You can verify these on the site as well that they both result in a 'match'
Go to https://bcrypt-generator.com/ and open your browser console.
Enter these lines into the console:
> var jwt1 = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk1OTg2MDIsImV4cCI6MTY0MjE5MDYwMn0.aJlzFHhBMGO4J7vlOudqOrOFnL1P-yEGrREgdaCXlxU" < undefined > var jwt2 = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk2MDY4ODgsImV4cCI6MTY0MjE5ODg4OH0.vo4HKLXuQbT0Yb0j21M4xl-rakxyE5wINjuGdkPuSJY" < undefined > var h = "$2a$10$z4rwnyg.cVtP2SHt3lYj7.aGeAzonmmzbxqCzi2UW3SQj6famGaqW" < undefined
Then enter these lines into the console, observe how they return
true
:> bcrypt.compareSync(jwt1, h) < true > bcrypt.compareSync(jwt2, h) < true
This is my own JS code that also reproduces the hash match:
// Login Logic
const refresh_token: string = jwt.sign({ userId }, authSecrets.refresh_secret, { expiresIn: '30d' });
const hash_refresh = bcrypt.hashSync(refresh_token);
await UserModel.update({
id: user.id,
refresh_token: hash_refresh,
});
// Refresh logic
// 'value' is the payload after using joi to validate it
const claims: any = jwt.verify(value.refresh_token, authSecrets.refresh_secret);
user = await UserModel.get(claims.userId);
if (!bcrypt.compareSync(value.refresh_token, user.refresh_token)) {
// This never happens with any JWT!
return response(401, 'Refresh Token is incorrect');
}
Why is this happening? the strings are clearly different (although not by a lot).