Day 08 - MERN: Authentication, Security, and bcrypt

Welcome to Day 8! Security is the backbone of any application. Today, we are organizing our routing structure and tackling the critical concepts of safely storing user passwords using hashing, salting, and peppering.

1. Modular Routing

Putting every route in index.js quickly creates a massive, unreadable file. By using Express Routers, we separate our routes by feature (like users, posts, or products) into a dedicated routes folder.

// routes/userRouter.js
import express from "express";
const userRouter = express.Router();

userRouter.post("/register", registerUser);
userRouter.post("/login", loginUser);

export default userRouter;

You then connect this router to your main application:

// index.js
import userRouter from "./routes/userRouter.js";
app.use("/api/users", userRouter); 
// Now POST /api/users/register works!

2. Defining the User Model

Before we can authenticate a user, we need a Mongoose blueprint (Schema) detailing how their data is structured in the database.

import mongoose from "mongoose";

const userSchema = new mongoose.Schema({
    name: String,
    email: String,
    password: String // We will NEVER store the actual password here!
});

export default mongoose.model("User", userSchema);

3. The Security Trio: Hash, Salt, and Pepper

If your database is ever compromised, storing plain-text passwords (like abc123) is a catastrophic failure. We secure them using three distinct layers:

  • Hashing: A one-way mathematical function that scrambles the password into a fixed string (e.g., $2b$10$...). Unlike encryption, a hash cannot be reversed or decrypted.
  • Salting: If two users have the identical password abc123, their resulting hashes would look identical, making them vulnerable. A salt is a random, unique value added to a specific user’s password before hashing so every hash is completely unique.
  • Peppering: A single, highly-secret value added to all passwords before hashing. Unlike salts (which are stored in the database alongside the hash), the pepper is stored completely separately (like in an environment variable).

4. Securing Passwords with bcrypt

bcrypt is the industry-standard library for hashing passwords in Node.js. It handles salt generation and hashing automatically for you.

Registration (Hashing): When a user registers, you must hash their password before saving the document to MongoDB.

import bcrypt from "bcrypt";

// The '10' is the cost factor (determines the computational complexity)
const passwordHash = bcrypt.hashSync(req.body.password, 10); 

const user = new User({
    name: req.body.name,
    email: req.body.email,
    password: passwordHash // Storing the safe hash, not the plain text!
});
await user.save();

Login (Comparing): When a user attempts to log in, you cannot decrypt the database hash to check it. Instead, you use bcrypt to mathematically compare the newly entered password against the stored hash.

// 1. Find the user by their email
const user = await User.findOne({ email: req.body.email });

// 2. Check if the entered password matches the stored hash (Returns true/false)
const isPasswordCorrect = bcrypt.compareSync(req.body.password, user.password);

if (!isPasswordCorrect) {
    return res.status(401).json({ message: "Invalid password" });
}
res.json({ message: "Login successful!" });

Share: X / Twitter LinkedIn Reddit WhatsApp

Comments

Questions, corrections, and practical takeaways are welcome here.