Introduction
Password storage is one of the most important security decisions when building a backend application.
A common mistake developers make is storing passwords using fast hashing algorithms like:
SHA256(password)
MD5(password)
These algorithms are designed to be fast, which makes them perfect for data integrity checks but terrible for password storage.
If your database is leaked, attackers can use GPUs and password-cracking tools to test billions of passwords.

Modern password hashing algorithms solve this problem by intentionally making password cracking expensive.
The three most common choices in Node.js applications are:
- Argon2id
- bcrypt
- PBKDF2
This article explains how each works and how to implement them correctly in Node.js applications.
1. Password Hashing Flow in a Node.js Application
A secure authentication system follows this flow:
User Registration
|
|
v
User enters password
|
|
v
Generate random salt
|
|
v
Password Hashing Algorithm
|
|
v
Store Hash in Database
Example database:
{
"email": "user@example.com",
"password": "$argon2id$v=19$m=65536,t=3,p=4$..."
}
During login:
User Login
|
|
v
Enter Password
|
|
v
Retrieve Stored Hash
|
|
v
Verify Password
|
|
v
Allow / Reject Login
2. Argon2id Implementation in Node.js
What is Argon2id?
Argon2id is currently the recommended password hashing algorithm for new applications.
It was designed to resist:
- GPU cracking attacks
- Password dictionary attacks
- Large-scale offline attacks
Unlike bcrypt and PBKDF2, Argon2id is memory-hard.
That means an attacker cannot simply add more GPUs to increase cracking speed.
Installing Argon2
Install the package:
npm install argon2
Basic Argon2id Password Hashing
Example:
const argon2 = require("argon2");
async function hashPassword(password){
const hash = await argon2.hash(password,{
type: argon2.argon2id
});
return hash;
}
async function verifyPassword(password,hash){
return await argon2.verify(
hash,
password
);
}
Argon2id Configuration
Production systems should configure the cost parameters.
Example:
const hash = await argon2.hash(password,{
type: argon2.argon2id,
memoryCost: 65536,
timeCost: 3,
parallelism: 4
});
Understanding Argon2 Settings
memoryCost
Controls RAM usage.
Example:
memoryCost: 65536
means:
65536 KB
≈
64 MB RAM
Higher value:
More security
+
More server memory usage
timeCost
Number of iterations.
Example:
timeCost:3
means:
Password processing happens 3 times
parallelism
Number of CPU threads.
Example:
parallelism:4
means:
Use 4 parallel lanes
Recommended Node.js Production Config
const ARGON_CONFIG = {
type: argon2.argon2id,
memoryCost: 65536,
timeCost: 3,
parallelism: 4
};
Complete User Registration Example
const argon2 = require("argon2");
async function registerUser(password){
const passwordHash =
await argon2.hash(
password,
ARGON_CONFIG
);
await User.create({
password: passwordHash
});
}
Database stores:
$argon2id$v=19$m=65536,t=3,p=4$...
The configuration is stored inside the hash.
You do not need to save:
memoryCost
timeCost
parallelism
separately.
Login Verification
async function login(password,storedHash){
const valid =
await argon2.verify(
storedHash,
password
);
if(valid){
return "Login success";
}
throw Error("Invalid password");
}
3. bcrypt Implementation in Node.js
What is bcrypt?
bcrypt is one of the oldest and most widely used password hashing algorithms.
It is still secure when configured correctly.
Many existing Node.js applications use bcrypt.
Installing bcrypt
npm install bcrypt
Basic bcrypt Example
const bcrypt = require("bcrypt");
async function hashPassword(password){
const saltRounds = 12;
return await bcrypt.hash(
password,
saltRounds
);
}
Understanding bcrypt Cost Factor
Example:
bcrypt.hash(password,12)
The number:
12
is the cost factor.
Calculation:
2^cost
Example:
cost 10
=
1024 rounds
cost 12
=
4096 rounds
Higher cost:
More secure
+
Slower login
Recommended bcrypt Configuration
const BCRYPT_ROUNDS = 12;
For high-security applications:
const BCRYPT_ROUNDS = 14;
bcrypt Verification
const match =
await bcrypt.compare(
userPassword,
databaseHash
);
if(match){
console.log("Authenticated");
}
bcrypt Limitations
bcrypt has one major limitation:
Maximum password length:
72 bytes
Example:
VeryLongPassword....................
Only first 72 bytes processed
For new systems, Argon2id is usually preferred.
4. PBKDF2 Implementation in Node.js
What is PBKDF2?
PBKDF2 is a password-based key derivation algorithm.
It is commonly used in:
- Enterprise systems
- Banking systems
- Compliance environments
Node.js already includes PBKDF2 through the built-in crypto module.
No package installation required.
PBKDF2 Example
const crypto = require("crypto");
function hashPassword(password){
const salt =
crypto.randomBytes(16)
.toString("hex");
const hash =
crypto.pbkdf2Sync(
password,
salt,
600000,
64,
"sha256"
);
return {
salt,
hash:
hash.toString("hex")
};
}
PBKDF2 Configuration
crypto.pbkdf2Sync(
password,
salt,
iterations,
keyLength,
digest
)
Example:
600000
means:
600,000 iterations
Recommended PBKDF2 Settings
{
iterations:600000,
keyLength:64,
algorithm:"sha256"
}
PBKDF2 Verification
function verifyPassword(
password,
salt,
storedHash
){
const hash =
crypto.pbkdf2Sync(
password,
salt,
600000,
64,
"sha256"
);
return (
hash.toString("hex")
===
storedHash
);
}
5. Node.js Configuration Comparison
| Algorithm | Package | Configuration |
|---|---|---|
| Argon2id | argon2 | memory, iterations, parallelism |
| bcrypt | bcrypt | salt rounds |
| PBKDF2 | crypto | iterations, key length |
6. Environment-Based Configuration
Do not hardcode security settings.
Example:
.env
PASSWORD_ALGORITHM=argon2id
ARGON_MEMORY=65536
ARGON_TIME=3
ARGON_PARALLELISM=4
BCRYPT_ROUNDS=12
PBKDF2_ITERATIONS=600000
Configuration file:
module.exports={
argon2:{
memoryCost:
Number(process.env.ARGON_MEMORY),
timeCost:
Number(process.env.ARGON_TIME),
parallelism:
Number(process.env.ARGON_PARALLELISM)
},
bcrypt:{
rounds:
Number(process.env.BCRYPT_ROUNDS)
}
};
7. Migrating bcrypt Users to Argon2id
You don't need to reset every password.
Migration flow:
User Login
|
Check bcrypt hash
|
Password correct?
|
Generate Argon2id hash
|
Replace old hash
Example:
if(await bcrypt.compare(password,user.password)){
const newHash =
await argon2.hash(
password,
ARGON_CONFIG
);
user.password=newHash;
await user.save();
}
Migration happens automatically.
8. Final Recommendation for Node.js Developers in 2026
New Projects
Use:
Argon2id
Example stack:
Node.js
+
Express
+
PostgreSQL/MongoDB
+
Argon2id
+
JWT/Session
+
MFA
Existing Applications
If you already use bcrypt:
Keep bcrypt
+
Increase cost factor
+
Gradually migrate to Argon2id
Enterprise Applications
If compliance requires it:
PBKDF2-HMAC-SHA256
Final Security Checklist
A production authentication system should have:
✅ Argon2id/bcrypt/PBKDF2
✅ Unique salt per password
✅ Rate limiting
✅ Account lockout protection
✅ MFA support
✅ Password breach detection
✅ Secure session management
✅ HTTPS everywhere
Conclusion
For Node.js applications in 2026:
Argon2id is the default choice for new systems.
bcrypt remains reliable for existing applications, while PBKDF2 continues to be useful where compliance standards require it.
The best implementation is not only choosing a strong hashing algorithm but also correctly configuring it, monitoring performance, and combining it with other authentication security layers.



Top comments (0)