Cookie Session Management

Last Updated : Oct 2026


Storing JWTs in httpOnly cookies protects them from Cross-Site Scripting (XSS) attacks because client-side JavaScript cannot access them. Using a short-lived Access Token alongside a long-lived Refresh Token maximizes security: even if an Access Token is intercepted, its brief lifespan minimizes damage.

For Understanding, we make a simple Authentication System.Where a user can register, login and logout.

So let's get started :

Installing Dependencies

npm install express jsonwebtoken cookie-parser dotenv

.env File

PORT=3000
ACCESS_TOKEN_SECRET=your_access_token_super_secret_key_123
REFRESH_TOKEN_SECRET=your_refresh_token_super_secret_key_456

Token Generation utils/jwt.js

const jwt = require("jsonwebtoken");

// Access Token: Short-lived (e.g., 15 minutes)
export const generateAccessToken = (user) => {
  return jwt.sign(
    { id: user.id, role: user.role },
    process.env.ACCESS_TOKEN_SECRET,
    { expiresIn: "15m" },
  );
};

// Refresh Token: Long-lived (e.g., 7 days)
export const generateRefreshToken = (user) => {
  return jwt.sign({ id: user.id }, process.env.REFRESH_TOKEN_SECRET, {
    expiresIn: "7d",
  });
};

Authentication & Cookie Routes (routes/auth.js)

import express from "express";
import jwt from "jsonwebtoken";
import { generateAccessToken, generateRefreshToken } from "../lib/jwt.js";

const router = express.Router();

// Mock database and token whitelist/store
const usersDB = [
  {
    id: 1,
    email: "tukkaLearn@example.com",
    password: "hashed_password",
    role: "user",
  },
];
let refreshTokensDB = []; // In production, store this in Redis or a Database!

// POST /api/auth/login
router.post("/login", (req, res) => {
  const { email, password } = req.body;
  const user = usersDB.find((u) => u.email === email);

  if (!user) return res.status(401).json({ error: "Invalid credentials" });

  const accessToken = generateAccessToken(user);
  const refreshToken = generateRefreshToken(user);

  // Save refresh token in database (allows revocation)
  refreshTokensDB.push(refreshToken);

  // Set Refresh Token in an httpOnly Cookie
  res.cookie("refreshToken", refreshToken, {
    httpOnly: true, // Prevents XSS reading the cookie via JS
    secure: process.env.NODE_NODE_ENV === "production", // HTTPS only in prod
    sameSite: "strict", // Prevents CSRF attacks
    maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days in milliseconds
  });

  // Return Access Token in JSON body (stored in memory on frontend)
  res.json({ accessToken });
});

// POST /api/auth/refresh (Obtain new Access Token using Cookie)
router.post("/refresh", (req, res) => {
  const refreshToken = req.cookies.refreshToken;

  if (!refreshToken) {
    return res.status(401).json({ error: "Refresh token missing" });
  }

  // Check if token exists in database whitelist
  if (!refreshTokensDB.includes(refreshToken)) {
    return res.status(403).json({ error: "Invalid or revoked refresh token" });
  }

  jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, decoded) => {
    if (err)
      return res
        .status(403)
        .json({ error: "Expired or invalid refresh token" });

    const user = usersDB.find((u) => u.id === decoded.id);
    if (!user) return res.status(404).json({ error: "User not found" });

    // Issue a new Access Token
    const newAccessToken = generateAccessToken(user);
    res.json({ accessToken: newAccessToken });
  });
});

// POST /api/auth/logout
router.post("/logout", (req, res) => {
  const refreshToken = req.cookies.refreshToken;

  // Remove from database store
  refreshTokensDB = refreshTokensDB.filter((token) => token !== refreshToken);

  // Clear cookie
  res.clearCookie("refreshToken", {
    httpOnly: true,
    sameSite: "strict",
    secure: process.env.NODE_ENV === "production",
  });

  res.json({ message: "Logged out successfully" });
});

export default router;

Server

import express from "express";
import cookieParser from "cookie-parser";
import dotenv from "dotenv";
import authRoutes from "./routes/auth.js";

dotenv.config();

const app = express();

app.use(express.json());
app.use(cookieParser()); // Parses incoming cookies into req.cookies

app.use("/api/auth", authRoutes);

app.listen(3000, () => console.log("Server running on port 3000"));

Liked This Tutorial

Scan QR Code To Leave A Review Or

Click Here To Leave A Review
Scan QR