A
O hashPassword includes the salt used. I practically answered that https://pt.stackoverflow.com/a/204641/15089 , but since the question wasn't specifically about that, then I think I can answer here too.The same way that "works in the Node" is the same that works anywhere, since it implements the same BCrypt.BCrypt is deterministic. For this reason you will have the same result if using the same salt, as well as any existing KDF. In BCrypt it has three parameters:Computational cost.Salt.Password.There are other derivative functions (KDFs) that have other input parameters. Argon2, for example, has individual cost parameters (for memory, iteration and etc.), parameters output length. On the other hand, the HKDF does not have computational cost, because it is not intended for passwords, but hash length, the result, flexible.As a result you will have:"Hash."However, the first two values are public in the case of BCrypt. Therefore, BCrypt itself uses a format, similar to http://passlib.readthedocs.io/en/stable/modular_crypt_format.html#overview , where:${Algoritmo}${Custo}${Salt}{Hash}
Thus the hash is included in values that allow you to compare the same hash, since we know the algorithm, the cost, the salt and the expected result.A small test, with a result of:$2b$10$111111111111111111111u1Fg3CCqE4CYDkm0w0C9gxJ.HpXSNHlS
You can get the algorithm, the cost, the salt and the "hash expected", so: let resultado = '$2b$10$111111111111111111111u1Fg3CCqE4CYDkm0w0C9gxJ.HpXSNHlS';
let algo = resultado.substring(1, 3);
let custo = resultado.substring(4, 6);
let salt = resultado.substring(7, 29);
So just do it bcrypt.hash and you can compare the value, normally:const crypto = require('crypto');
const bcrypt = require('bcrypt');
async function init(plainPassword) {
let resultado = '$2b$10$111111111111111111111u1Fg3CCqE4CYDkm0w0C9gxJ.HpXSNHlS';
let algo = resultado.substring(1, 3);
let custo = resultado.substring(4, 6);
let salt = resultado.substring(7, 29);
let hash = await bcrypt.hash(plainPassword, '$' + algo + '$' + custo + '$' + salt);
console.log(crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(resultado)));
}
init('cba');
See that there is no bcrypt.compare, crypto.timingSafeEqual do the same as the ===, however it is safe against timing-attack. That's basically what bcrypt.compare does, when you include hashPassword it already has the necessary data.