One rule
Never store a user’s password. Store a slow password hash created by a trusted library.
A password hash is one-way. During login, you hash-check the typed password against the stored hash.
Why slow hashes
Normal hashes like SHA-256 are too fast for passwords. If your database leaks, an attacker can try crores of guesses quickly.
Password hash functions like bcrypt and Argon2 are built to be slow. They also include a unique salt so two users with the same password get different hashes.
Do not do this
Do not encrypt passwords so you can decrypt them later. Your app never needs to know the original password again.
Do not write your own salting or hashing logic. Use a maintained library and store the full hash string it returns.
Signup flow
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done
Node example
This example uses bcrypt in an Express-style app. The same idea applies in Next.js, FastAPI, Django, Rails, or Laravel.
Install with npm install bcrypt. If bcrypt gives install trouble on your laptop, bcryptjs can work for learning, but use bcrypt or Argon2 in serious projects.
import bcrypt from "bcrypt";
const SALT_ROUNDS = 12;
export async function createUser(email, password) {
const passwordHash = await bcrypt.hash(password, SALT_ROUNDS);
const user = await db.user.create({
data: {
email,
passwordHash
}
});
return { id: user.id, email: user.email };
}
export async function loginUser(email, password) {
const user = await db.user.findUnique({ where: { email } });
if (!user) return null;
const ok = await bcrypt.compare(password, user.passwordHash);
if (!ok) return null;
return { id: user.id, email: user.email };
}What to store
Name the database column passwordHash, not password. That small choice helps reviewers see you did the right thing.
Store the full hash string returned by the library. With bcrypt, it contains the algorithm, cost, salt, and hash in one string.
AI check
If you ask ChatGPT or Gemini for auth code, check the output carefully. Reject code that uses SHA-256, MD5, crypto.createHash, reversible encryption, or a hardcoded salt for passwords.
Also check that signup stores only passwordHash, and login uses bcrypt.compare or the equivalent verify function.
Do this now
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done