RU Send your project
All articles

Sign-in and sessions: where AI-generated auth goes wrong

Tokens in localStorage or cookies, sessions that never expire, leaky password resets, and permission checks that only exist in the UI.

· 4 min readSecurityCode

The login form is the first thing an assistant builds in any app, and it always looks convincing. Email field, password field, button, redirect to the dashboard. The problems live behind the form: where the token is stored, how long it lives, what happens on password reset, and who on the server checks that the person is allowed to do what they’re asking.

Token in localStorage or in a cookie

Assistants almost always put the token in localStorage. It’s the easiest option and works immediately. The cost is that any script on the page can read it. One XSS bug or one compromised analytics library, and the token is in someone else’s hands, used from someone else’s machine.

A cookie with HttpOnly, Secure and SameSite=Lax can’t be read from JavaScript, so XSS can’t steal it. In exchange you get a different concern, CSRF: the browser sends the cookie on its own, so state-changing requests need protecting. SameSite covers most cases; a CSRF token covers the rest.

There’s no universal answer, but for a typical web app with its own backend, cookies are usually the safer choice. If you stay with localStorage, at least keep the token short-lived and take XSS seriously.

Sessions that never expire

A frequent find: a JWT with no exp claim, or one that expires in a year. That token is a permanent pass. Logging out removes it from the browser, but the server keeps accepting it if anyone kept a copy.

What you need:

  • A short-lived access token (minutes to hours) and a separate refresh token you can revoke.
  • Server-side session storage or a revocation list, so “log out” and “log out everywhere” actually do something.
  • All sessions revoked when the password or email changes.

Password reset

This is where mistakes cost the most, because a reset is, by definition, a way in without a password.

  • The link is reusable and never expires. A six-month-old email in someone else’s inbox should be useless. Expiry in minutes or hours, one use only.
  • The token is guessable. A user ID, a timestamp or a short number instead of a cryptographically random string.
  • The link is built from the Host header. An attacker requests a reset for your email with a forged header, and you receive a genuine email linking to their domain. Take the site URL from config, never from the request.
  • The response reveals whether the email exists. “User not found” turns the form into a tool for checking other people’s addresses. Answer the same way every time: “if that address exists, we’ve sent an email”.
  • Old sessions survive the reset. Someone resets their password because they suspect a break-in, and whoever broke in stays logged in.

Checks that only exist in the UI

The most common and most dangerous mistake: permissions are enforced by the interface. The Admin button is hidden unless the profile has an isAdmin flag, and the flag lives in localStorage or arrives from the client in the request body.

Signs of it:

  • Role or plan is read from something the browser sent.
  • Middleware protects pages but not the API. In Next.js it’s easy to configure matcher so that /api isn’t covered.
  • The server checks that someone is logged in, but not that the record belongs to them.

The rule is simple: every server handler works out who is calling from the session, not from a field in the request, and decides on its own whether they’re allowed. The UI only hides buttons so people don’t get confused.

Small things that aren’t small later

Changing the email without re-entering the password. Linking a social login to an existing account by an unverified address. Email verification you can skip by navigating to another page. Each is a line of code, and each one opens someone else’s account.

If an assistant wrote your auth and nobody has read it since, start there. SUBXCAT reviews exactly these spots and fixes them without rewriting your whole sign-in.

Want us to do this for you?

Tell our assistant about your project — a rough estimate in five minutes, a review in 48 hours.

Book an intro call 20 minutes: an engineer, a designer and a marketer look at your project with you.
Book a call