3 minute read Security

If there’s one thing nearly every developer has built at some point, it’s a login system. And why not? There are plenty of libraries to help, standards like OAuth and JWTs to lean on, and modern frameworks that abstract most of the grunt work. But that’s exactly why authentication is such a deceptively dangerous part of your stack.

Many of the security incidents I’ve seen didn’t happen because developers didn’t try to secure authentication. They happened because teams thought they already had.

Let’s walk through some of the most common pitfalls I see in real-world systems, and how attackers quietly slip through the cracks.

Pitfall #1: JWTs Without Proper Expiry or Revocation

JSON Web Tokens (JWTs) are everywhere now, especially in SPAs and mobile apps. They’re fast, self-contained, and stateless. But here’s the problem: a lot of engineers treat them as “set it and forget it.”

I’ve seen production tokens valid for 24 hours, 7 days, even 30 days. If an attacker gets their hands on a token — via XSS, local storage access, or an intercepted request — that’s potentially weeks of uninterrupted access. No MFA prompt, no password check, no way to revoke the token on the server. It just works… for the attacker.

Fix:

  • Use short expiry (e.g. 15 minutes) + refresh tokens with rotation.
  • Store a token ID server-side and revoke it on logout or password change.
  • Avoid putting JWTs in localStorage — use HttpOnly cookies with CSRF protection. Otherwise they are vulnerable to XSS bugs.

Pitfall #2: Confusing Authentication with Authorisation

Let’s say a user logs in and hits an endpoint like /invoice/98732. You check that they’re logged in, fetch the invoice from the database, and return it. Seems fine — until someone logs in and changes the ID to /invoice/98733.

If you aren’t checking whether that user owns the invoice, they can see someone else’s billing history. That’s not just a mistake — that’s a data breach.

Authentication confirms identity. Authorisation determines what that identity can do. Too many systems blur the two, and attackers know exactly where to look.

Fix:

  • Always enforce object-level access controls (is this user allowed to access this resource?).
  • Never rely on the client to hide or filter data.

Pitfall #3: Insecure Password Reset Flows

Attackers don’t always go through the front door. Sometimes they walk in through the “forgot password” flow.

Weak reset links (predictable, long-lived, or reused across sessions) are gold to an attacker. So are reset emails delivered over HTTP, or forms that don’t expire reset tokens properly.

One of the worst cases I’ve seen involved a user resetting their password, only to have a previous session still valid in another browser tab — where an attacker had access. The user thought they were secure. The attacker quietly stayed logged in.

Reset flows should be treated with the same care as login itself.

Fix:

  • Use short-lived, single-use, unpredictable tokens.
  • Invalidate all sessions/tokens after a password reset.
  • Use secure email delivery and HTTPS on all reset pages.

Pitfall #4: Blind Trust in OAuth or SSO Providers

OAuth and OpenID Connect can simplify auth, but they aren’t foolproof — and if you treat them like they are, you’re inviting trouble.

I’ve seen applications accept unsigned tokens, skip validation of the issuer (iss) or audience (aud), or blindly trust email addresses just because they came from Google or Microsoft. In one case, a dev environment was left open to any user with a @company.com address, without any internal access checks.

Federated login should never replace your own authorisation logic. It tells you who someone is — it doesn’t tell you what they should be allowed to do.

Fix:

  • Validate every token’s signature and claims.
  • Never auto-provision roles or permissions based solely on email domain.
  • Treat federated auth as just step one — enforce your own access logic.

Pitfall #5: Improper Session Management

Session management is often overlooked. Sure, the user can log in and out, but what happens to all their other sessions? Are old ones being cleaned up? Are you tracking which devices they logged in from? What happens when they change their password?

In too many systems, nothing happens. The attacker logs in once — maybe by phishing or stealing a cookie — and that session just lives forever. Even if the victim resets their password, the attacker’s tab stays warm and ready.

Sessions need boundaries. Expire them after a reasonable period. Invalidate them on password changes or MFA changes. Give users visibility and control over where they’re logged in from.

Fix:

  • Invalidate sessions on logout, password change, or suspicious behavior.
  • Add optional re-authentication for critical actions (e.g. password change).
  • Detect and alert on new devices or geographies.

Leave a comment