Dynavo Labs
HomeAbout Us
Service
Case StudiesInsightsContact
Get Quote
Dynavo Labs

We design and build digital products that grow businesses — websites, mobile apps, and everything in between.

Services

  • Software Development
  • Web Development
  • Mobile Development
  • AI Development
  • Quality Assurance
  • DevOps & Cloud

Company

  • About
  • Industries
  • Technology
  • Contact

Resources

  • Insights
  • Case Studies

© 2026 dynavo Labs. All rights reserved.

Privacy PolicyTerms & Conditions
dynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labsdynavo Labs

JWT vs Session Authentication: Which One to Use

How JWT and session authentication work, where each one is stronger, how to store them safely, and a plain rule for choosing one for a web app or an API.

By Muzammil•October 31, 2026•6 min read•Software Development•Backend Development
JWT vs Session Authentication: Which One to Use

Every application that has user accounts has to answer one question on every request: who is this? Two approaches dominate: server-side sessions and JSON Web Tokens (JWT). Both work, and both are used wrongly often.

This guide explains how each one works, what each costs you, and how to choose.

How Session Authentication Works

  1. The user logs in with a password.
  2. The server creates a session record, which holds the user ID and an expiry, in a store such as a database or Redis.
  3. The server sends the browser a cookie containing a random session ID.
  4. On each request, the browser sends the cookie, and the server looks the session up.
// Login
const sessionId = crypto.randomUUID()
await sessions.set(sessionId, { userId: user.id }, { ttl: 60 * 60 * 24 * 7 })

res.cookie('sid', sessionId, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  maxAge: 7 * 24 * 60 * 60 * 1000,
})

// Later requests
const session = await sessions.get(req.cookies.sid)
if (!session) return res.status(401).end()

The cookie holds no user data, only a reference. Everything that matters stays on the server.

How JWT Authentication Works

  1. The user logs in with a password.
  2. The server creates a signed token containing claims such as the user ID and an expiry.
  3. The client sends the token with each request, usually in an Authorization header or a cookie.
  4. The server verifies the signature and reads the claims. It does not need to look anything up.
import jwt from 'jsonwebtoken'

// Login
const token = jwt.sign({ sub: user.id }, process.env.JWT_SECRET, {
  algorithm: 'HS256',
  expiresIn: '15m',
})

// Later requests
try {
  const claims = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] })
  req.userId = claims.sub
} catch {
  return res.status(401).end()
}

A JWT has three parts: a header, a payload and a signature. The payload is encoded, not encrypted. Anyone who holds the token can read it, so never put secrets in it.

Side by Side

SessionsJWT
StateStored on the serverIn the token
Per-request costA store lookupA signature check
Revoke a loginDelete the session, effective immediatelyHard until the token expires
Scales across serversNeeds a shared storeAny server with the key can verify
Data in the clientOnly an opaque IDReadable claims
SizeSmall cookieLarger, grows with claims
Natural fitServer-rendered web appsAPIs, mobile apps, service-to-service

The Revocation Problem

This is the largest difference in practice. With a session, logging a user out, banning an account or forcing a password reset means deleting a row. The effect is immediate.

A JWT is valid until it expires, wherever it is. To revoke one before then, you need a denylist, and a denylist that is checked on every request is a server-side store again, which gives back the benefit that made JWT attractive.

The usual way to live with this is a short-lived access token, around 5 to 15 minutes, and a longer-lived refresh token. The refresh token is stored server-side and can be revoked. The damage from a stolen access token is limited by its short life.

Where to Store the Token in a Browser

This choice decides how exposed you are.

StorageReadable by page JavaScriptSent automaticallyMain risk
localStorageYesNoXSS steals the token
Cookie with HttpOnlyNoYesCSRF, handled with SameSite and tokens

Storing tokens in localStorage is common in tutorials and a poor default. Any cross-site scripting bug lets an attacker read the token and use it elsewhere. An HttpOnly, Secure, SameSite cookie keeps the token out of reach of page scripts. That advice applies to a session ID and to a JWT equally.

Security Checklist

For both approaches:

  • Serve everything over HTTPS and set the Secure flag.
  • Set HttpOnly on any cookie that carries a credential.
  • Set SameSite=Lax or Strict, and add CSRF protection for state-changing requests when using cookies.
  • Hash passwords with a slow algorithm such as argon2 or bcrypt. Never store them in plain text.
  • Rate-limit login attempts.

Specific to JWT:

  • Pin the allowed algorithm when verifying. Never accept whatever the token header claims, and never accept none.
  • Keep the signing key out of the code and rotate it on a schedule.
  • Always set an expiry and keep it short.
  • Check the issuer and audience if more than one service issues tokens.

Specific to sessions:

  • Generate IDs with a cryptographically secure random source, and make them long.
  • Issue a new session ID after login to prevent session fixation.
  • Expire idle sessions.

Which One Should You Use?

Choose sessions when:

  • You are building a conventional web app that the browser talks to directly.
  • You want instant logout and easy account lockout.
  • One database or Redis instance is acceptable to your architecture.

Choose JWT when:

  • Several independent services must verify a user without sharing a session store.
  • A mobile app or third-party client calls your API.
  • You use a standard that is built on tokens, such as OpenID Connect.

For many teams the honest answer is sessions. They are simpler to reason about, easier to revoke and harder to get wrong. A JWT is a good tool for a specific problem, which is verifying identity across systems without a shared lookup, and it is a worse choice when it is used only because it is popular.

Using Both

A common pattern combines them: a session or refresh token at the identity provider, and short-lived JWTs passed to downstream services. The provider can revoke the session, and the services verify tokens without calling it.

Common Mistakes

  • Treating a JWT as encrypted. The payload is readable by anyone who has the token.
  • Long expiry with no revocation. A token valid for 30 days is a 30-day exposure.
  • Storing the token in localStorage. One XSS bug exposes every user.
  • Not pinning the verification algorithm. It opens the door to forged tokens.
  • Putting changing data in the token. Roles and permissions in a JWT go stale until it expires.
  • Rolling your own crypto or session logic. Use a maintained library or your framework's auth support.

Summary

Sessions keep state on the server and make revocation easy. JWTs keep state in a signed token and make verification stateless. For a typical web app, start with sessions in an HttpOnly cookie. Reach for JWTs when you need to verify identity across services or clients that cannot share a session store.

#Authentication#JWT#Sessions#Security#Backend Development#Web Security

Responses

M↓

Get Started on a Project?

Let's Talk

Start the
Journey
dynavolabs.info@gmail.comStart the Journey