This is a smart move. When you're in the "high-intensity" phase of learning, documenting the "why" and the "where" is the only way to make sure the knowledge sticks.
Here is a comprehensive README.md structure for your authentication system.
This repository implements a full MERN-logic authentication flow (Node/Express/Postgres) using Passport.js for session management and Bcrypt for security.
βββ app.js # Entry point: Middlewares & Server config
βββ config/
β βββ passport.js # The "Brain": Strategy & Session Logic
βββ controllers/
β βββ authController.js # The "Handler": Request/Response logic
βββ models/
β βββ authModel.js # The "Librarian": Raw Database queries
βββ routes/
β βββ authRouter.js # The "Traffic Controller": Route definitions
βββ services/
β βββ authService.js # The "Worker": Business logic (Hashing)
βββ middleware/
βββ authMiddleware.js # The "Bouncer": Protecting routes
Goal: Initialize the "Storage Locker" (Session) and the "Bouncer" (Passport).
CRITICAL: The order of
app.usematters. Session must come before Passport!
const session = require('express-session');
const passport = require('passport');
require('./config/passport'); // Run the config logic
// 1. Session setup
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: { maxAge: 1000 * 60 * 60 * 24 } // 1 Day
}));
// 2. Passport setup
app.use(passport.initialize());
app.use(passport.session()); Goal: Define how to verify a user and how to store them in a cookie.
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');
const authModel = require('../models/authModel');
passport.use(new LocalStrategy(async (username, password, done) => {
try {
const user = await authModel.findUserByUsername(username);
if (!user) return done(null, false, { message: "User not found" });
const match = await bcrypt.compare(password, user.password);
if (!match) return done(null, false, { message: "Wrong password" });
return done(null, user); // Success!
} catch (err) { return done(err); }
}));
// STORE ID in cookie
passport.serializeUser((user, done) => done(null, user.id));
// READ ID from cookie & fetch full user
passport.deserializeUser(async (id, done) => {
try {
const user = await authModel.findUserById(id);
done(null, user);
} catch (err) { done(err); }
});Raw SQL queries. Return null if user isn't found to avoid "truthy" string bugs.
const findUserByUsername = async (username) => {
const { rows } = await db.query('SELECT * FROM users WHERE username = $1', [username]);
return rows[0] || null;
};Used for Sign-Up only. Handles hashing.
const signup = async (username, password) => {
const hash = await bcrypt.hash(password, 10);
return await authModel.createUser(username, hash);
};exports.signUp = async (req, res) => { /* call service */ };
exports.logIn = (req, res) => res.json({ user: req.user });
exports.logOut = (req, res) => {
req.logout((err) => res.redirect('/'));
};Connects the logic to the endpoints.
router.post('/signup', authController.signUp);
// Trigger the Strategy here:
router.post('/login', passport.authenticate('local'), authController.logIn);
router.get('/logout', authController.logOut);Protecting private data.
const isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) return next();
res.status(401).json({ message: "Unauthorized" });
};- Module Error? Run
npm install express-session passport passport-local bcrypt. - 500 Session Error? Check if
app.use(session(...))is aboveapp.use(passport.session()). - User Not Found? Ensure
authModelreturnsnullinstead of a string like"not found". - Bcrypt Error? Ensure you are comparing
password(plain) withuser.password(hash), not the other way around.
π‘οΈ Role-Based Access Control (RBAC) isAuthenticated: The entry gate. Ensures a session cookie is valid.
authorize('role_name'): The VIP list. Ensures the user's role in the DB matches the required access level.
Status Codes:
401 Unauthorized: I don't know who you are (Go to Login).
403 Forbidden: I know who you are, but you aren't allowed in here.
- Signup: Manual (Service -> Model -> DB).
- Login: Automatic (Passport Strategy ->
serializeUser-> Session Cookie). - Persistence: On every request,
deserializeUserconverts the cookie ID back intoreq.user.