OWASP Top 10:2025 — A07 Authentication Failures for AI-Generated Code

Written by the Rafter Team

The fastest way to ship a broken login in 2026 is to ask an LLM for one. "Add email/password auth to my Next.js app" returns a confident, well-formatted answer that hashes passwords (sometimes), sets a cookie (rarely with the right flags), and emits a reset-password endpoint that emails a four-byte token with no expiration. The code compiles. It passes the happy path. It is also the front door wide open. A07 is the category where AI codegen is most dangerous, because authentication looks like a solved problem and reads like boilerplate, so nobody — human or model — re-derives it from the threat model on each generation.
What A07 actually is
A07 covers anything that lets the wrong person become an authenticated user, or stay one longer than they should. The textbook list is weak credentials, broken session management, missing MFA, and exploitable password-reset flows. The concrete version: an LLM-written /api/login route that compares passwords with == instead of a constant-time compare, has no rate limit, issues a JWT with exp: null, and stores it in localStorage so any XSS lifts every active session. Each line looked fine in isolation. The combination is account takeover with a script.
Why AI-generated code trips on it
LLMs are trained on a decade of tutorials, half of which were wrong when they were written. When you ask for "user authentication," the model averages over StackOverflow snippets, framework quickstarts, and outdated Medium posts. That average is reliably below the security floor of any production auth system. A few specific patterns show up over and over in AI-generated code:
- Roll-your-own auth instead of the platform's. The model writes a
userstable, abcrypt.hashcall, and ajsonwebtoken.signline — when Supabase Auth, Clerk, or NextAuth would have done the same job with rotation, lockout, and session invalidation built in. - Reset tokens that aren't random, aren't expiring, or aren't single-use.
Math.random().toString(36)shows up shockingly often. So does "store the token in the user row and never clear it." - JWTs with no expiry, no audience claim, and
alg: noneaccepted on verify — the classic 2018-era bug, still generated in 2026. - Cookies without
HttpOnly,Secure,SameSite. The model knows the option names; it forgets to set them unless you ask. - No MFA, no rate limit, no lockout, no logging. These are negative space — the model only adds them if prompted, because the prompt rarely asks for them.
Express + raw pg, Flask + SQLAlchemy, and FastAPI + custom user models are the worst offenders. Next.js with NextAuth and anything with Supabase or Clerk wired in tend to come out closer to safe, but only if the LLM uses the library instead of reinventing it next to it.
The fix on agentic CLIs (Claude Code, Codex)
You have file access. Use it. Before merging any AI-written auth code, re-prompt the agent with an explicit checklist instead of trusting the first pass. A prompt that consistently produces fixable diffs:
Audit every auth-related file in this repo against OWASP A07:2025.
For each file, report:
- Password hashing: algorithm, cost factor, constant-time compare on verify
- Session/JWT: expiry, rotation, where it's stored, cookie flags
- Reset flow: token entropy, expiry, single-use, channel
- Rate limiting and lockout on login + reset
- MFA presence and enforcement
For each gap, propose the minimal diff using whatever auth library
is already in package.json. If none, recommend one. Do not roll our own.
Then run the diffs through your normal review. Rafter's findings UI has a Copy-for-AI button next to every A07 finding — it emits a prompt that names the file, the line, the specific weakness, and the safe replacement pattern for your framework, ready to paste into Claude Code or Codex. That closes the loop from "scanner found it" to "agent fixed it" without you re-deriving the fix.
One non-negotiable: after the agent applies the patch, read the diff. Auth bugs hide in three-line changes.
The fix on opinionated platforms (base44, Greta, OpenClaw)
If you build on base44, Greta, Replit Agent, or a similar opinionated platform, you usually can't open auth.ts and edit it. You prompt the platform's agent, and it edits its own internals. That changes the tactic but not the requirements.
What to tell the platform, in plain language:
- "Use the platform's built-in auth provider for everything, including reset and MFA. Do not store passwords in my app's tables." Most of these platforms ship a managed auth module. The failure mode is the agent building a parallel
userstable next to it because the prompt was vague. - "Require MFA for any account with admin or billing permissions. Block login until MFA is enrolled for those roles."
- "All sessions must expire after 24 hours of inactivity and 7 days absolute. Log out everywhere on password change."
- "Rate-limit login and password-reset endpoints. Lock the account after 10 failed attempts in 15 minutes and notify the user by email."
- "Password reset links must expire in 15 minutes, be single-use, and be invalidated when used or when the password changes."
Then verify by trying it. Use a throwaway account. Reset the password twice with the same link — the second attempt must fail. Log in with a wrong password 11 times — you must get locked out. If the platform's agent says it did the thing but the behavior says otherwise, the agent is wrong; re-prompt with the observed failure as the bug report. Platforms get auth roughly right out of the box only if you make the requirements explicit. The default assumption — "the platform handles it" — is how vibe-coded apps end up on haveibeenpwned.
See also
- A01 Broken Access Control — what an authenticated attacker can reach once they get past A07.
- A04 Cryptographic Failures — password hashing, token signing, and the crypto primitives that A07 leans on.
- A09 Security Logging and Alerting Failures — without login-event logging, you won't notice the credential-stuffing run against the endpoint you just hardened.