Suppose the requirement is to keep users logged in after closing their browser, while allowing each device to be logged out separately. Where should a JWT or session ID live? localStorage, sessionStorage, and cookies each have tradeoffs. Persistence in one browser is separate from logging in on another device; a storage choice alone does not provide cross-device authentication.
So I compared four representative options and thought through why things ultimately settle on a single approach. Session information is, in effect, a hotel room key. Where do you keep it, and who can deactivate it? Look at it through those two questions and things get surprisingly tidy.
The Four Options
First, there is putting a JWT in localStorage. The value survives closing the browser, so on persistence it's flawless. But its contents are fully visible to JavaScript, which makes it terribly weak against XSS (cross-site scripting).
If a malicious script runs even once, the token walks out the door that very moment. It's like hiding your key under the potted plant in the hallway. If you care about security at all, this one is the first to go.
Another option is a JWT in an HttpOnly cookie. SettingMax-Age or Expires allows persistence after the browser closes. HttpOnly prevents JavaScript from reading the value directly, but does not stop injected scripts from performing authenticated actions. Each cookie attribute protects a different part of the interaction.
Revocation is the operational concern. A design that checks only a JWT’s signature and expiry has difficulty logging out a particular device immediately. It resembles a guest carrying a spare key absent from the front desk’s register. A revocation list or session lookup can provide that control, at the cost of server-side state and management.
A third option is a session ID in an HttpOnly cookie. Cookie expiry controls persistence, while Secure restricts transmission to HTTPS. Preventing direct access to the value and preventing XSS itself remain separate concerns.
Where it particularly shines is operational flexibility. The session itself lives on the server in Redis or a DB, so the server can invalidate it whenever it sees fit. It's the key card the front desk can wipe. Listing the devices currently logged in, or destroying every session when the password changes, becomes a trivial matter.
Finally, a JWT can be stored in sessionStorage. It normally disappears when its tab closes, making it unsuitable for this persistence requirement. Like localStorage, it is readable from JavaScript.
Laid side by side, they look like this.
| Storage | Persistence after browser closure | Direct JavaScript access | Server-side revocation |
|---|---|---|---|
| localStorage + JWT | Possible | Yes | Requires a revocation lookup or similar state |
| HttpOnly cookie + JWT | Depends on cookie expiry settings | No | Requires a revocation lookup or similar state |
| HttpOnly cookie + session ID | Depends on cookie expiry settings | No | Invalidate the stored session |
| sessionStorage + JWT | Normally until the tab closes | Yes | Requires a revocation lookup or similar state |
Why the Session ID Wins
For a typical web application that needs logout and device management, I would choose an HttpOnly cookie with a server-managed session ID. It makes storage protection and control after issuance easier to consider together. There are three reasons.
First, HttpOnly prevents direct reads by scripts. Second, invalidating the server-side session can stop access on the next authentication check. JWTs can also support revocation, but this arrangement keeps revocation within session management.
The third is device management. When a user is logged in on several devices, each session can be listed and deleted individually. That strength comes precisely from keeping state on the server.
Before choosing a JWT in localStorage for scalability, consider forced logout as well. Without a revocation mechanism, the system must wait for issued tokens to expire. Asking the front desk to recover spare keys is difficult when there is no register of them.
These four options differ in both storage and revocation. For this use case, I want to limit direct access to the credential and retain control after it has been issued. Cookies with server-side sessions make both concerns manageable. A key needs a revocation plan as well as a storage location.