We are aleady aware about Authorization and Authentication in last section AuthN vs AuthZ Concepts (using session+cookies)
Today we will learn how we can do Authorization and Authendcation using JWT Token.
Let first understand what is JWT Token?
A JWT is a lightweight and secure format for exchanging data between two parties (such as a client and server) in the form of a JSON object.
In modern web applications, it’s mostly used for authentication (confirming who the user is) and authorization (deciding what they can access).
👉 For example: Think of JWT like an ID card.
- When you log in, the system issues you a JWT, just like an office issues you an ID card.
- The ID card contains key information (your name, employee ID, department) — in JWT, this information is stored as claims.
- Whenever you want to enter a secure area (make a request), you just show your ID card (send the JWT).
- The security guard (server) doesn’t need to call HR each time — they just check the card’s validity (JWT signature) and allow/deny access.
Structure of a JWT
A JWT has 3 parts separated by dots (.):
HEADER.PAYLOAD.SIGNATURE
- Header
- Contains metadata about the token:
- The algorithm used (e.g., HS256 = HMAC-SHA256).
- The token type (JWT).
- Payload
- Contains the data (claims) we want to store.
- Claims can be: Registered claims: e.g., iss (issuer), exp (expiration time), sub (subject).
- Custom claims: e.g., id, role, email.
- Signature
- Ensures the token has not been tampered with.
- Created by combining:HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
You can check the validity of JWT token in this site jwt.io
Why use JWT?
- Stateless Authentication
- Server doesn’t need to store sessions.
- Token itself contains user data and validity.
- Secure Transmission
- Token is signed (cannot be tampered with).
- If using HTTPS, the token is secure in transit.
- Portable
- Can be used across different services (APIs, microservices).
JWT vs Session
Feature | Session + Cookies | JWT |
Storage | Server (in memory/DB) | Client-side (token itself) |
Scalability | Harder (session store needed) | Easier (stateless) |
Revocation | Easy (delete session) | Hard (must blacklist token) |
Best for | Traditional web apps | APIs / mobile apps / microservices |
Now will implement AuthN and AuthZ using JWT token.
Here is our Flow:
- User logs in (AuthN).
- Server issues JWT.
- On each request, middleware verifies token (AuthN).
- Middleware checks user role/permissions (AuthZ).
Step 1: Initialize project:
mkdir express-auth-demo
cd express-auth-demo
npm init -y
npm install express jsonwebtoken bcryptjs
Step 2: Create file server.js
We will have this code in server.js
const express = require("express");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");
const app = express();
app.use(express.json());
const SECRET = "mySecretKey";
// Fake user DB
const users = [
{ id: 1, username: "admin", password: bcrypt.hashSync("admin123", 8), role: "admin" },
{ id: 2, username: "user", password: bcrypt.hashSync("user123", 8), role: "user" }
];
// Login (AuthN)
app.post("/login", (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user) return res.status(404).send("User not found");
const isPasswordValid = bcrypt.compareSync(password, user.password);
if (!isPasswordValid) return res.status(401).send("Invalid password");
const token = jwt.sign({ id: user.id, role: user.role }, SECRET, { expiresIn: "1h" });
res.json({ token });
});
// Middleware to verify JWT
function verifyToken(req, res, next) {
const token = req.headers["authorization"];
if (!token) return res.status(403).send("No token provided");
jwt.verify(token.split(" ")[1], SECRET, (err, decoded) => {
if (err) return res.status(401).send("Unauthorized");
req.user = decoded;
next();
});
}
// Protected route
app.get("/profile", verifyToken, (req, res) => {
res.send(`Welcome User ID: ${req.user.id}, Role: ${req.user.role}`);
});
app.listen(3000, () => console.log("Server running on port 3000"));
Step 3: Testing
1. Run the server
node server.js
2. Use Postman:
POST /login with { "username": "admin", "password": "admin123" }
3. Copy returned JWT and now check this on jwt.io website. Remeber to write the secret key also on the jwt.io site. If our token is good to go then we can now implement Authorization
For authorization we will add a middleware code:
// Role-based AuthZ
function authorizeRole(role) {
return (req, res, next) => {
if (req.user.role === role) {
next();
} else {
res.status(403).send("Forbidden: You don’t have access");
}
};
}
// Only admin can access
app.get("/admin", verifyToken, authorizeRole("admin"), (req, res) => {
res.send("Welcome Admin");
});
// Only normal user can access
app.get("/user", verifyToken, authorizeRole("user"), (req, res) => {
res.send("Welcome User");
});
Testing:
Steps to Send JWT in Postman
- Open Postman → New Request
- Choose GET (or the method of your protected route).
Example:GET http://localhost:3000/admin
Go to Authorization Tab
- In Postman, select the request → Click Authorization tab.
- In Type, select: Bearer Token.
- In the Token field, paste the JWT you copied.
Send the Request
- If your token is valid → You’ll get the protected resource.
- If invalid/expired → You’ll get 401 Unauthorized or 403 Forbidden.