Session vs Token Authentication

Compare stateful session cookies and stateless JWT tokens, evaluating their storage costs, verification paths, revocation limits, and security trade-offs.

BeginnerAPI DesignChapter: API Design10 min read

The Concept

Authentication is the mechanism by which a server identifies a user requesting resources. When an API receives a request, it must verify the sender's identity. Historically, web applications relied on stateful session systems where the server remembered each logged-in client. With the rise of horizontal scalability and microservices, stateless token models like JSON Web Tokens (JWT) emerged.

The core architectural trade-off is simple:

  • Stateful Sessions prioritize centralization and control. The server holds the source of truth, making token revocation instant, but requiring database queries for every API request.
  • Stateless Tokens prioritize performance and scale. The user holds their own claims in a cryptographically signed token. The server validates the token using local CPU operations without hitting a database, but cannot easily revoke a compromised token.

Session-Based vs Token-Based Authentication FlowStateful Session AuthClientApp ServerSession Store1. Request + Cookie (Session ID)2. Look up ID3. Return User Data4. HTTP 200 OK (Data)Stateless Token AuthClientApp Server1. Request + JWT in Header2. CryptographicSignature Check3. HTTP 200 OK (Data)


Practical Analogy

Authentication models mirror the differences between event tickets and hotel key cards:

  • Stateful Sessions are like a hotel key card. The card itself has a simple identifier. Every time you scan it on a door lock, the lock queries the hotel's central database to see if room 204 is still rented by you, if your payment went through, and if your checkout time has passed. If you lose your card or get evicted, the receptionist deactivates the key card immediately in the central system.
  • Stateless Tokens are like a printed concert ticket. The ticket contains explicit assertions: "Seat 12B, concert date: June 15th, signed by Ticketmaster." The ticket checker at the door validates the barcode signature with their hand scanner, checking the ticket's internal math validity. The checker does not call Ticketmaster's central server for every attendee. However, if you steal someone's ticket, they cannot easily deactivate it without creating a complex blocklist of stolen serial numbers at the door.

Stateful Session Authentication

In the Stateful Session model, the server holds the session record. When a client logs in, the server generates a unique, cryptographically random string, the session ID. The server writes this ID to its memory, database, or high-performance cache (e.g. Redis). It then sends the session ID back to the client, typically packaged in an HTTP Set-Cookie header.

  • Client storage: The browser receives the cookie and automatically attaches it to every subsequent HTTP request to that domain.
  • Server verification: When the client requests a secure profile page, the server extracts the session ID from the Cookie header. It must query its session cache or database to find the user profile mapping.

The primary advantage is control: because the session is evaluated on every request, the server can terminate a session instantly (e.g. if the user clicks logout or their password is changed). The main disadvantage is scaling: if an API receives thousands of requests per second, the state store becomes a read bottleneck, requiring database scaling or caching strategies.


Stateless Token Authentication

The Stateless Token model eliminates the server-side lookup. During authentication, the server generates a token (usually a JWT) containing the user's details, authorization claims, and expiration date. The server signs this data block using a secret key (symmetric HMAC) or a private key (asymmetric RSA/ECDSA).

The client receives the token and stores it, typically in browser local storage or in memory, sending it to the server in the Authorization: Bearer <token> header of subsequent API requests.

The server verifies the request by extracting the token and running a cryptographic math check:

  • It reads the public key or shared secret.
  • It recalculates the signature of the payload and header.
  • It compares the calculated signature with the one in the token.
  • It verifies the expiration time is in the future.

This verification path is purely computational. No databases are queried, enabling horizontal scaling: if you have ten application servers, any of them can verify the token without sharing a session store.


Verification and Validation Paths

The contrasting paths highlight why token-based systems are popular in microservices:

Feature Stateful Sessions Stateless Tokens (JWT)
Verification Method State lookup (database/cache read) Cryptographic signature validation (CPU computation)
Scalability Harder: requires centralized database or distributed cache Easy: share public verification keys across servers
Revocation Instant: remove session ID from storage Latent: must wait for expiration or use a blocklist
Data Payload Minimal: only a random ID string Larger: contains header, payload, and signatures

Security Profiles: Cookies vs LocalStorage

Authentication security depends heavily on how the credentials are stored on the client side:

Cookie Protections

Sessions are typically stored in cookies, which benefit from native browser security features:

  • HttpOnly: Prevents client-side scripts (such as JavaScript) from reading the cookie, neutralizing the risk of credential theft via Cross-Site Scripting (XSS).
  • Secure: Forces the browser to transmit the cookie only over encrypted HTTPS connections.
  • SameSite: Controls whether cookies are sent on cross-site requests, mitigating Cross-Site Request Forgery (CSRF) attacks. Set to Strict or Lax to block external scripts from executing ambient credentialed requests.

Token Protections

Tokens are often stored in localStorage or sessionStorage because JavaScript APIs must read them to build request headers.

  • XSS Vulnerability: If an attacker succeeds in running malicious JavaScript on the page, they can immediately read the token from localStorage and transmit it to an external server.
  • CSRF Mitigation: Because tokens are not automatically sent by the browser like cookies, they are immune to typical CSRF attacks. The client must explicitly read the token and set the authorization header.

Revocation and Lifetime Management

Because stateless tokens are verified locally, they cannot be instantly revoked. If an administrator disables an account, or if a user logs out, the client might discard the token from local storage, but the token itself remains cryptographically valid. Anyone who intercepted the token can continue making valid API calls until it expires.

To mitigate this limitation, APIs use a hybrid strategy:

  • Short-Lived Access Tokens: Access tokens expire quickly (e.g. 5 to 15 minutes), minimizing the window of vulnerability if a token is stolen.
  • Long-Lived Refresh Tokens: A separate refresh token is stored securely (e.g. in a secure, HttpOnly cookie). When the access token expires, the client calls a /refresh endpoint. The refresh endpoint is stateful: it checks the database to confirm the user is still active and returns a new access token.
  • Revocation Blocklists: For immediate revocation, servers can maintain a Redis cache of revoked token IDs (jti claim). Although this introduces a state lookup, it is limited to the subset of explicitly revoked tokens, keeping verification overhead low.

Further Reading

Prerequisites

Code Examples

Core Literature References

Session Management Cheat Sheet

by OWASP Foundation — Cookie-Based Session Management vs Token-Based Authentication

View source