š„ Introduction
In this tutorial, you will learn how to build a complete Node.js JWT authentication system with login, signup, protected routes, and secure logout using a token blacklist system.
JWT is stateless, so we manually manage logout using blacklist storage (memory or Redis in production).
š¦ Install Dependencies
Install Packages
npm init -y
npm install express mongoose jsonwebtoken bcryptjs dotenv cors
š️ User Model (MongoDB)
User Model
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: String,
email: { type: String, unique: true },
password: String
});
module.exports = mongoose.model("User", userSchema);
š JWT Token Generator
JWT Generator
const jwt = require("jsonwebtoken");
function generateToken(user) {
return jwt.sign(
{ id: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: "1h" }
);
}
module.exports = generateToken;
š§ Token Blacklist (Logout System)
This is used to invalidate JWT tokens after logout.
Blacklist Store
const blacklistedTokens = new Set();
module.exports = blacklistedTokens;
šŖ Login & Signup API
Auth API
const express = require("express");
const bcrypt = require("bcryptjs");
const User = require("./models/User");
const generateToken = require("./utils/token");
const router = express.Router();
// SIGNUP
router.post("/signup", async (req,res)=>{
const { name, email, password } = req.body;
const hash = await bcrypt.hash(password,10);
await User.create({
name,
email,
password: hash
});
res.json({message:"User created"});
});
// LOGIN
router.post("/login", async (req,res)=>{
const user = await User.findOne({email:req.body.email});
if(!user) return res.status(404).send("Not found");
const ok = await bcrypt.compare(req.body.password,user.password);
if(!ok) return res.status(401).send("Wrong password");
const token = generateToken(user);
res.json({token});
});
module.exports = router;
š”️ Middleware (Auth + Blacklist Check)
Auth Middleware
const jwt = require("jsonwebtoken");
const blacklistedTokens = require("./blacklist");
module.exports = function(req,res,next){
const token = req.headers.authorization?.split(" ")[1];
if(!token) return res.status(401).send("No token");
if(blacklistedTokens.has(token))
return res.status(401).send("Logged out token");
try {
req.user = jwt.verify(token,process.env.JWT_SECRET);
next();
} catch(e){
res.status(401).send("Invalid token");
}
};
šŖ Logout API (Blacklist Token)
Logout API
const blacklistedTokens = require("./blacklist");
router.post("/logout",(req,res)=>{
const token = req.headers.authorization?.split(" ")[1];
if(token) blacklistedTokens.add(token);
res.json({message:"Logged out"});
});
š Protected Route Example
Protected Route
const auth = require("./middleware/auth");
router.get("/profile",auth,(req,res)=>{
res.json({
message:"Welcome",
user:req.user
});
});
š„ Conclusion
You just built a production-level Node.js authentication system with JWT, secure login/signup, protected routes, and blacklist-based logout system.
š Next upgrade: Use Redis instead of memory for scalable blacklist handling in production apps.
