When implementing authentication in a web service, you choose between sessions and JWT. Sessions let the server manage state for immediate control. JWT keeps no state on the server, favoring horizontal scaling. The core question is “where does the authentication state live.”

When a logged-in user requests the next page, the server does not know who they are. HTTP is stateless. To maintain authentication, state must be stored somewhere.

Session Authentication

Session authentication means the server directly manages the user’s authentication state.

When a user logs in, the server creates session data and issues a unique session ID. This session ID is delivered to the client via a cookie. On each subsequent request, the cookie carries the session ID, and the server looks it up in the session store to identify the user.

Session data is stored in server memory, the file system, or an external store like Redis.

Strengths

The server manages sessions directly, making control straightforward. A specific user’s session can be invalidated immediately. Features like forced logout or concurrent session limits are easy to implement.

Only the session ID reaches the client, so user information faces less exposure risk over the network.

Limitations

Storing state on the server constrains horizontal scaling. When multiple server instances run, a user routed to a different instance cannot find their session. Solving this requires sticky sessions or a shared session store like Redis.

As user count grows, session store load grows with it.

JWT

JWT (JSON Web Token) embeds authentication information in the token itself. The server stores no state.

Structure

A JWT consists of three parts, separated by dots.

header.payload.signature

The header specifies the token type and signing algorithm. The payload contains claims such as user identification and expiration time. The signature is the header and payload signed with a secret key.

When the server receives a token, it verifies the signature. If the payload has been tampered with, the signature will not match, revealing forgery. No session store lookup is needed.

Strengths

The server stores no state, so horizontal scaling is unrestricted. Any server instance can verify the token. No separate session store is required.

Limitations

Once issued, a token is difficult to invalidate before expiration. The server holds no token state. Forced logout requires a separate blacklist store, in which case the stateless advantage diminishes.

The payload is Base64-encoded, not encrypted. Sensitive information must not be placed in the payload.

Token size exceeds a session ID. Since the token is transmitted with every request, network overhead is larger than the session approach.

Access Token and Refresh Token

With JWT, issuing a single token creates a trade-off in expiration settings. A long expiration is risky if the token is stolen. A short expiration forces users to re-authenticate frequently.

Splitting the token into two solves this.

The access token authenticates API requests. Its expiration is set short. Even if stolen, it expires quickly.

The refresh token is used to obtain a new access token. Its expiration is relatively long. The server can store and manage it, enabling invalidation when needed.

The renewal flow:

  1. The client sends an API request with the access token.
  2. When the access token expires, the server returns 401.
  3. The client requests a new access token using the refresh token.
  4. The server validates the refresh token and issues a new access token.

Storage Strategies

Session IDs are stored in cookies by convention. JWT has multiple options, and each carries different security characteristics.

Memory

Stored in a JavaScript variable. Lost on page refresh. Not exposed to XSS attacks, but requires re-authentication on every refresh.

localStorage

Stored in the browser’s storage. Persists through refreshes. However, JavaScript can access it, making it vulnerable to XSS. If XSS occurs, the token can be stolen.

Setting the HttpOnly attribute prevents JavaScript access. This blocks direct token theft via XSS. However, CSRF attacks require separate mitigation. Combining the SameSite attribute with a CSRF token is standard practice.

StorageSurvives RefreshXSS ResistanceCSRF Resistance
MemoryNoNot exposedN/A
localStorageYesVulnerableN/A
cookie (HttpOnly)YesNot exposedRequires mitigation

A common combination stores the access token in memory and the refresh token in an HttpOnly cookie. The access token’s short lifespan limits exposure risk. HttpOnly on the refresh token blocks XSS theft.

Comparison

AspectSessionJWT
State storageServerClient (token)
Horizontal scalingShared store neededUnrestricted
Immediate invalidationEasyDifficult (blacklist needed)
Network sizeSmall (session ID)Large (full token)
Server loadStore lookupSignature verification (CPU)

Service structure and requirements determine the choice. Whether immediate invalidation is essential, and whether the cost of sharing state across servers is acceptable, are the key decision points. When immediate control matters in a single-server environment, sessions fit. When authentication must work across distributed servers without shared state, JWT fits.