Sitemap
3 min readJun 29, 2025

--

Press enter or click to view image in full size

Why You Should Never Store JWTs in localStorage: A Simple Guide to Safer Authentication

Many web apps today use JWTs (JSON Web Tokens) for login and authentication. A common way to store these tokens is in localStorage or sessionStorage because it’s quick and easy.

But here’s the problem:
🛑 This method is not safe.

If you’re storing JWTs this way, your app could be vulnerable to serious attacks. Let me explain why and show you a better, more secure alternative.

🚨 The Risks of Storing Tokens in localStorage/sessionStorage

1. XSS Attacks (Cross-Site Scripting)

Anything stored in localStorage or sessionStorage is accessible via JavaScript. That means if a hacker injects a malicious script into your page (via a third-party library or browser extension), they can steal your token.

Unlike httpOnly cookies, which JavaScript can’t read, localStorage is wide open.

2. Tokens Don’t Expire Automatically

Cookies can be set to expire after a certain time. But localStorage data stays there forever unless you manually delete it. If a hacker steals your JWT, they can use it until you change your backend logic or rotate keys.

3. Switching to Cookies? Watch Out for CSRF

Some devs switch to cookies for better security but that introduces CSRF (Cross-Site Request Forgery) risks unless you:

  • Set SameSite=Strict
  • Use CSRF tokens on state-changing requests

✅ A Safer Alternative: BFF (Backend-for-Frontend) + Redis

To avoid all these problems, many teams now use a pattern called BFF (Backend-for-Frontend) along with server-side token storage (like Redis).

Here’s how it works:

🔐 The Frontend Never Sees the JWT

  • The backend handles authentication, stores JWTs in Redis, and sends the frontend only an httpOnly cookie with a session ID.
  • This cookie is not accessible to JavaScript so no chance of XSS.
  • All token management happens on the server.

🔁 How the Flow Works

  • Login: User submits login form → BFF checks credentials → JWTs are created and stored in Redis → frontend receives an httpOnly cookie.
  • API Request: Frontend sends the cookie → BFF looks up tokens in Redis → forwards request to backend API.
  • Refresh Token: When the JWT expires, the BFF uses the refresh token (stored securely in Redis) to get a new one automatically.
  • Logout: BFF deletes the session from Redis → token is instantly invalidated.

🧠 Why Redis?

  • Speed: Redis is extremely fast (sub-millisecond), perfect for token lookups.
  • Expiry: Redis supports auto-expiry (TTL), so you don’t need to manually delete old tokens.
  • Revocability: You can invalidate tokens anytime by just deleting them from Redis, something you can’t do with normal stateless JWTs.

✅ Key Benefits

  • No XSS Risk: JWTs never touch the browser. JavaScript can’t steal what it can’t see.
  • Better Session Control: Use short-lived access tokens (e.g., 15 mins) and long-lived refresh tokens (e.g., 7 days) for better balance between security and user experience.
  • Scalable: Redis can handle millions of sessions. Perfect for large-scale apps.

🧰 When Should You Use This?

This setup is great for:

  • Single Page Apps (SPAs) built with React, Vue, or Angular
  • Mobile apps where token theft is a risk
  • Microservices architectures where token validation needs to be centralized

🛠️ Final Thoughts

Yes, localStorage is convenient — but it’s not secure for storing authentication tokens.

🔒 The BFF + Redis approach gives you:

  • Strong security (no XSS or CSRF)
  • Smooth UX (silent refreshes, instant logout)
  • Flexibility and performance at scale

📝 What to Do Next

  • ✅ Use httpOnly + Secure + SameSite cookies
  • ✅ Store tokens in Redis with persistence (RDB/AOF)
  • ✅ Consider token binding (e.g., tie tokens to IP or device ID)

--

--

No responses yet