Authentication with JWT in MERN (Step-by-Step Guide)
Authentication is a must-have for nearly every web application. In the MERN stack (MongoDB, Express, React, Node), JWT (JSON Web Tokens) is the go-to method for handling stateless, secure login systems.
In this guide, you'll learn how to implement user authentication with JWT in a MERN stack app β step-by-step.
π What is JWT?
JWT (JSON Web Token) is a compact, URL-safe token format used to securely transmit information between parties.
It has three parts:
- Header
- Payload
- Signature
Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
π§± Folder Structure for Authentication
Assume you're using a clean MERN structure like:
mern-auth/
βββ client/ # React
βββ server/ # Node + Express
βββ controllers/
βββ models/
βββ routes/
βββ middleware/
π Backend Setup (Express + JWT)
1. Install dependencies
npm install express mongoose bcryptjs jsonwebtoken dotenv cors
2. Create User Model (models/User.js
)
const mongoose = require("mongoose"); const UserSchema = new mongoose.Schema({ name: String, email: { type: String, unique: true }, password: String, }); module.exports = mongoose.model("User", UserSchema);
3. Create Auth Controller (controllers/auth.js
)
const jwt = require("jsonwebtoken"); const bcrypt = require("bcryptjs"); const User = require("../models/User"); exports.register = async (req, res) => { const { name, email, password } = req.body; const hashed = await bcrypt.hash(password, 10); const user = await User.create({ name, email, password: hashed }); res.json(user); }; exports.login = async (req, res) => { const { email, password } = req.body; const user = await User.findOne({ email }); const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) return res.status(400).json({ error: "Invalid credentials" }); const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: "1h" }); res.json({ token }); };
4. JWT Middleware (middleware/auth.js
)
const jwt = require("jsonwebtoken"); module.exports = (req, res, next) => { const token = req.headers["authorization"]; if (!token) return res.status(403).send("Access Denied"); try { const verified = jwt.verify(token.split(" ")[1], process.env.JWT_SECRET); req.user = verified; next(); } catch (err) { res.status(400).send("Invalid Token"); } };
5. Auth Routes (routes/auth.js
)
const express = require("express"); const { register, login } = require("../controllers/auth"); const router = express.Router(); router.post("/register", register); router.post("/login", login); module.exports = router;
βοΈ Frontend (React)
- Use
axios
to send login/register requests - Store JWT in
localStorage
or HTTP-only cookies - Protect routes using conditional rendering or
useEffect
axios.post("/api/login", { email, password }) .then(res => localStorage.setItem("token", res.data.token));
πΌοΈ Suggested Image Prompt:
β Final Notes
- Always hash passwords using
bcrypt
- Donβt store JWTs in localStorage for sensitive apps (use HTTP-only cookies)
- Set token expiry and refresh logic for long sessions
π Summary
JWT is lightweight, secure, and scalable β perfect for MERN apps.
With this setup, your app is ready for user login, protected APIs, and secure sessions in 2025 and beyond.
Published on: 2025-06-18