Stop 10 of 42 · The ID Desk · Session & token storage

Visitor badges left lying around

After the ID check, Alice gets a visitor badge. This bank drops badges in an open basket by the door, never writes an expiry date on them, and lets them be shown from anywhere. Anyone who reaches into the basket can walk around as Alice, for weeks.

Severity Medium to HighCategory Auth WeaknessThe ID DeskSources checked 25 Oct 2026

Watch it happen

Play it as

Alice’s badge sits in an open basket with no expiry. Mallory copies it and later uses it at the vault door.

Arrival

Alice walks into the bank to do her business. First stop is the ID desk.

1 / 5

In bank terms and app terms

In the bankIn your app
Visitor badgeYour session cookie or login token
Open basket by the doorlocalStorage / sessionStorage, readable by any script on the page
Sealed lanyard pocketA cookie with HttpOnly, Secure and SameSite set
Expiry date on the badgeShort token lifetime (exp), plus refresh and sign-out

Are you exposed?

You might be exposed if…
  • Your code calls localStorage.setItem("token", …) or "access_token", or stores a JWT in a JS-readable cookie.
  • Your session cookie is missing HttpOnly, Secure or SameSite in the browser’s cookie list.
  • Logins last for days or weeks, or tokens have no exp at all.
What to do about it
  1. Store sessions in HttpOnly, Secure, SameSite cookies set by the server, not in localStorage.
  2. Keep access tokens short-lived (minutes) and use refresh tokens that you can revoke.
  3. Add real logout that invalidates the session on the server, not just deletes it in the browser.

Track these in your to-do list →

What it looks like in code

// GOOD: Use httpOnly cookies instead of localStorage
response.cookies.set("session", token, {
  httpOnly: true,   // JS can't read it
  secure: true,     // HTTPS only
  sameSite: "lax",  // CSRF protection
  maxAge: 3600      // 1 hour expiration
})

// GOOD: Short-lived JWTs with refresh tokens
const token = jwt.sign(payload, SECRET, { expiresIn: "15m" })
const refreshToken = jwt.sign({ userId }, REFRESH_SECRET, { expiresIn: "7d" })

Ask your AI tool to fix it

Paste this into Cursor, Claude, Lovable, Bolt or whatever you build with.

Prompt
Find everywhere my app stores auth tokens or session IDs. Move them out of localStorage into HttpOnly, Secure, SameSite=Lax cookies set by the server, shorten access-token lifetime to 15 minutes with a refresh mechanism, and make logout invalidate the session server-side. If I use Supabase or Firebase, use their server-side session helpers and tell me which cookie flags they set by default and which I can change.

Where this comes from

The facts, figures and fixes on this page were checked against these sources on 25 Oct 2026. They are linked so you can check for yourself.

Spot a mistake or something out of date? Report a correction · How these guides are made and checked

Go deeper in isitsecure

The free, open-source scanner checks for most of the problems in these guides.