Node.js cookbook · Intermediate

Hash passwords and implement login safely in Node.js

Store user passwords securely and build a login endpoint resistant to brute force and enumeration.

Approach

Hash with Argon2id (argon2 package) or bcrypt, verify on login, rehash when parameters change, return the same error for unknown users and wrong passwords (and verify against a dummy hash to equalize timing), rate-limit attempts, and rotate the session on login.

Solution

import argon2 from "argon2";

const HASH_OPTIONS = { type: argon2.argon2id, memoryCost: 19_456, timeCost: 2, parallelism: 1 };   // OWASP baseline
const DUMMY_HASH = await argon2.hash("not-a-real-password", HASH_OPTIONS);

export async function register(email: string, password: string) {
  if (password.length < 12) throw new HttpError(422, "Validation failed", "Password must be at least 12 characters");
  const hash = await argon2.hash(password, HASH_OPTIONS);
  return users.create({ email: email.toLowerCase(), passwordHash: hash });
}

app.post("/api/auth/login", loginLimiter, async (req, res) => {
  const { email, password } = req.body as { email: string; password: string };
  const user = await users.findByEmail(String(email).toLowerCase());
  const ok = await argon2.verify(user?.passwordHash ?? DUMMY_HASH, String(password));
  if (!user || !ok) return res.status(401).json({ title: "Invalid email or password" });

  if (argon2.needsRehash(user.passwordHash, HASH_OPTIONS)) {
    await users.updatePasswordHash(user.id, await argon2.hash(password, HASH_OPTIONS));
  }
  req.session.regenerate((err) => {                      // prevent session fixation
    if (err) return res.status(500).end();
    req.session.userId = user.id;
    res.json({ id: user.id, email: user.email });
  });
});

Pitfalls

  • SHA-256/MD5 password hashes.
  • Distinct errors that reveal registered emails.
  • No rate limiting or lockout strategy.

Variations

  • Offer passkeys (WebAuthn) via @simplewebauthn/server.
  • Delegate auth to an identity provider (Auth0, Keycloak, Cognito).

Further reading: OWASP Password Storage Cheat Sheet

Reference: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

More Node.js recipes