Password Hashing
The Password Hashing component provides secure, battle-tested utilities for hashing and verifying user passwords.
It supports multiple algorithms so you can choose the right trade-off between security, performance, and compatibility.
Supported algorithms:
- argon2 (recommended)
- bcryptjs
- scrypt
- pbkdf2
Installation Guide
Install the component using the ServerCN CLI:
npx servercn add password-hashingBasic Implementation
You can place the following helpers in your project:
MVC: src/helpers/auth.helpers.ts or src/helpers/auth.ts
Feature: src/modules/auth/auth.helpers.ts
Choose one password hashing strategy based on your security and performance requirements.
1. argon2 ✅
A modern, memory-hard algorithm designed to resist GPU and ASIC attacks. Best choice for new applications
import argon2 from "argon2";
export async function hashPassword(password: string): Promise<string> {
return argon2.hash(password);
}
export async function verifyPassword(
password: string,
hash: string
): Promise<boolean> {
return argon2.verify(hash, password);
}2. bcryptjs
A widely adopted and battle-tested algorithm with configurable cost factor. Suitable for legacy compatibility.
import bcrypt from "bcryptjs";
export async function hashPassword(
password: string,
rounds = 12
): Promise<string> {
return bcrypt.hash(password, rounds);
}
export async function verifyPassword(
password: string,
hash: string
): Promise<boolean> {
return bcrypt.compare(password, hash);
}3. scrypt
A memory-intensive algorithm that significantly increases the cost of brute-force attacks.
import crypto from "node:crypto";
import { promisify } from "node:util";
const scryptAsync = promisify(
crypto.scrypt as (
password: crypto.BinaryLike,
salt: crypto.BinaryLike,
keylen: number,
options: crypto.ScryptOptions,
callback: (err: Error | null, derivedKey: Buffer) => void
) => void
);
const DEFAULTS = {
keyLength: 64,
saltLength: 16,
N: 16384,
r: 8,
p: 1
};
export async function hashPasswordScrypt(
password: string,
options = DEFAULTS
): Promise<string> {
const salt = crypto.randomBytes(options.saltLength);
const derivedKey = await scryptAsync(password, salt, options.keyLength, {
N: options.N,
r: options.r,
p: options.p
});
return [
"scrypt",
options.N,
options.r,
options.p,
salt.toString("hex"),
derivedKey.toString("hex")
].join("$");
}
export async function verifyPasswordScrypt(
password: string,
storedHash: string
): Promise<boolean> {
const [algo, N, r, p, saltHex, hashHex] = storedHash.split("$");
if (algo !== "scrypt") {
throw new Error("Invalid scrypt hash format");
}
const salt = Buffer.from(saltHex, "hex");
const hash = Buffer.from(hashHex, "hex");
const derivedKey = await scryptAsync(password, salt, hash.length, {
N: Number(N),
r: Number(r),
p: Number(p)
});
return crypto.timingSafeEqual(hash, derivedKey);
}4. pbkdf2
A standards-based algorithm included in Node.js core. Useful when external dependencies are not allowed.
import crypto from "node:crypto";
const DEFAULTS = {
iterations: 310000,
keyLength: 64,
saltLength: 16,
digest: "sha512"
};
export function hashPasswordPbkdf2(
password: string,
options = DEFAULTS
): string {
const salt = crypto.randomBytes(options.saltLength);
const derivedKey = crypto.pbkdf2Sync(
password,
salt,
options.iterations,
options.keyLength,
options.digest
);
return [
"pbkdf2",
options.iterations,
options.digest,
salt.toString("hex"),
derivedKey.toString("hex")
].join("$");
}
export function verifyPasswordPbkdf2(
password: string,
storedHash: string
): boolean {
const [algo, iterations, digest, saltHex, hashHex] = storedHash.split("$");
if (algo !== "pbkdf2") {
throw new Error("Invalid PBKDF2 hash format");
}
const salt = Buffer.from(saltHex, "hex");
const hash = Buffer.from(hashHex, "hex");
const derivedKey = crypto.pbkdf2Sync(
password,
salt,
Number(iterations),
hash.length,
digest
);
return crypto.timingSafeEqual(hash, derivedKey);
}Recommendation
For most applications:
- Use Argon2 for new projects
- Use bcrypt only for compatibility with existing systems
- Prefer scrypt or PBKDF2 when external dependencies are restricted
Usage Example
/**
* The examples below demonstrate password hashing and verification
* using the argon2-based helpers from the Password Hashing component.
*/
import { hashPassword, verifyPassword } from "../helpers/auth";
export const hashPasswordUsage = (req, res) => {
const { password } = req.body;
const hashedPassword = await hashPassword(password);
return ApiResponse.ok(res, "Password hashed successfully");
};
export const verifyPasswordUsage = (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email }).select("+password");
if (!user) {
throw ApiError.unauthorized("Invalid email or password");
}
const isPasswordValid = await verifyPassword(password, user.password);
if (!isPasswordValid) {
throw ApiError.unauthorized("Invalid email or password");
}
return ApiResponse.ok(res, "Password verified successfully");
};