JWT (JSON Web Token) authentication issues a signed token at login and verifies that token on protected routes, so the server can trust who's making a request without storing session state.
Step 1: Install dependencies
npm init
npm install jsonwebtoken mongoose express
Step 2: Sign-up and login routes
const express = require("express");
const mongoose = require("mongoose");
const jwt = require("jsonwebtoken");
const User = require("./userModel");
const app = express();
app.use(express.json());
app.post("/signup", async (req, res, next) => {
const { name, email, password } = req.body;
const newUser = new User({ name, email, password });
try {
await newUser.save();
} catch {
return next(new Error("Could not create the account."));
}
const token = jwt.sign(
{ userId: newUser.id, email: newUser.email },
"library-secret-key",
{ expiresIn: "1h" }
);
res.status(201).json({
success: true,
data: { userId: newUser.id, email: newUser.email, token },
});
});
app.post("/login", async (req, res, next) => {
const { email, password } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ email });
} catch {
return next(new Error("Something went wrong."));
}
if (!existingUser || existingUser.password !== password) {
return next(new Error("Invalid email or password."));
}
const token = jwt.sign(
{ userId: existingUser.id, email: existingUser.email },
"library-secret-key",
{ expiresIn: "1h" }
);
res.status(200).json({
success: true,
data: { userId: existingUser.id, email: existingUser.email, token },
});
});
mongoose
.connect("mongodb://localhost:27017/librarydb")
.then(() => {
app.listen(3600, () => console.log("Server listening on port 3600"));
})
.catch(() => console.log("Database connection failed"));
Step 3: Verify the token on a protected route
app.get("/my-loans", (req, res) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ success: false, message: "Token missing" });
}
const token = authHeader.split(" ")[1]; // "Bearer <token>"
try {
const decoded = jwt.verify(token, "library-secret-key");
res.status(200).json({
success: true,
data: { userId: decoded.userId, email: decoded.email },
});
} catch {
res.status(401).json({ success: false, message: "Invalid or expired token" });
}
});
Why JWT
- Stateless — the server doesn't need to store session data.
- Scalable — works well across multiple server instances.
- Portable — the same token can be verified by any service that knows the secret (or public key, for asymmetric signing).