Here is an scrypt just in JS[1]. It will also run in the browser, if you need to hash client side for some reason. On node, it will use the built in crypto api which IIRC is a wrapper around openSSL so perf should be native. I'm not a big fan of the API, you have to concern yourself with buffers and normalization. Maybe that is important for non-latin alphabets? I don't actually know, I'm too anglo-centric.
Then there is good ol bcrypt[2]. Certainly the most mature, it has been around a while. Like [0], it also uses a native module with node-gyp.
All three support async/await so you can avoid blocking the event loop during expensive hashing operations. I should note I haven't really used any of them. I was just curious so I did some googling.
Based on your criteria, I think [0] fits the bill best. Very hard to forget the salt, as it is generated by default and stored with the hash.
I'm always really curious about other people's code, so I hope you don't mind me asking: I typically use a library/framework that handles details like password hashing. That said, sometimes i like to avoid libs and really understand every aspect. Can I inquire about your stack? Are you using express or koa or anything like that?
[0] https://github.com/ranisalt/node-argon2
I think it's fine enough if I stick with bcrypt, and that's what I'm planning to do, but I saw this was on the front page, so I opportunistically made the comment hoping that someone with experience in this area (e.g. tptacek) might see the question and jump in :)
[0] https://latacora.micro.blog/2018/04/03/cryptographic-right-a...
const scryptParams = { logN: 15, r: 8, p: 1 };
async function hashPassword(password: string): Promise<string> {
const password_hash: Buffer = await scrypt.kdf(password, scryptParams);
return password_hash.toString("base64");
}
async function verifyPassword(hash, password): Promise<boolean> {
return await scrypt.verify(Buffer.from(hash, "base64"), password);
}