hardFull Stack EngineerStartup
JWT vs Session-based authentication — what are the trade-offs, and when would you use each?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a startup building their auth system:
> "We're implementing authentication for our Next.js app. Should we use JWT tokens or server-side sessions? We have both SSR pages and a React SPA."
> "We're implementing authentication for our Next.js app. Should we use JWT tokens or server-side sessions? We have both SSR pages and a React SPA."
Suggested Solution
Session-Based Auth
// Login → server creates session
app.post("/login", (req, res) => {
const user = verifyCredentials(req.body);
const sessionId = crypto.randomUUID();
sessions.set(sessionId, { userId: user.id }); // Store in Redis
res.cookie("sid", sessionId, { httpOnly: true, sameSite: "strict" });
});
// Subsequent requests → server validates session
app.use((req, res, next) => {
const session = sessions.get(req.cookies.sid);
if (!session) return res.status(401).json({ error: "Unauthorized" });
req.user = session;
next();
});
JWT-Based Auth
// Login → server issues JWT
app.post("/login", (req, res) => {
const user = verifyCredentials(req.body);
const token = jwt.sign({ sub: user.id, role: user.role }, SECRET, { expiresIn: "7d" });
res.json({ token });
});
// Subsequent requests → client sends JWT
const token = localStorage.getItem("token");
fetch("/api/data", { headers: { Authorization: Bearer ${token} } });
// Server validates JWT (stateless — no session store)
const payload = jwt.verify(token, SECRET);
Comparison
Interview OS Approach (Hybrid)
// JWT stored in HttpOnly cookie (best of both worlds)
// - No CSRF risk (SameSite=Strict)
// - No XSS risk (HttpOnly, not in localStorage)
// - Stateless validation
// - Short expiry (1h) + refresh token (7d)
export async function createSession(user: User) {
const token = await jose.newEncryptJWT({ sub: user.id, role: user.role })
.setProtectedHeader({ alg: "dir", enc: "A256GCM" })
.setExpirationTime("1h")
.encrypt(JWT_SECRET);
cookies().set("session", token, {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 3600,
});
}