On this page
  1. Authentication vs authorization: The distinction that causes the bugs
  2. Choosing and designing: The decisions behind the login flow
  3. Authorization: Every object needs an owner check
  4. Login mechanics: Sessions, cookies, and tokens
  5. Credentials: Passwords, hashing, and the alternatives
  6. Federated login: OAuth and third-party identity
  7. Session hygiene: Headers, policies, and expiry
  8. Abuse controls: Rate limiting and account protection
  9. Testing auth: Proving the checks exist
  10. Common mistakes: The twelve that keep showing up
  11. Checklist
  12. FAQ
    1. Why can users see each other’s data in the app my AI built?
    2. Should I use sessions or JWTs?
    3. Is OAuth the right way to add ‘Sign in with Google’?
    4. Where should the authorization check live?
  13. Related topics
  14. Sources
guide

Authentication and Authorization for AI Builders: Who You Are, and What You're Allowed to Touch

Login, sessions, JWTs, OAuth, passkeys, and the object-level checks your AI assistant skips. Every auth concept a vibecoder needs, with a linked guide for each.

Quick answer

  • Authentication answers “who is this?” Authorization answers “what may they touch?” AI assistants implement the first and skip the second.
  • Broken access control is the most common serious flaw in AI-generated CRUD apps: the code checks login, not ownership.
  • How you store passwords, how long sessions last, and how tokens are revoked are decisions the assistant will make for you if you don’t.
  • JWTs are not automatically more secure than sessions. Revocation is the tradeoff, and most small apps don’t need it.
  • Test auth by trying to read someone else’s record with your own valid session — that’s the check that finds the real bug.
  • Every concept below links to a full guide. Start with access control, then login mechanics.

Authentication vs authorization: The distinction that causes the bugs

Authentication is proving identity: a password, a passkey, a token from a provider. Authorization is deciding what that identity may do. They’re implemented in different places and fail differently — an authentication bug locks people out, while an authorization bug quietly hands one user another user’s data.

AI assistants are reliably good at the first and unreliable at the second. Registration, login, password reset, and session handling are well-trodden patterns with obvious shapes to generate. Authorization is contextual: it depends on who owns what, which records are private, which roles exist, and which fields are meant to be visible. None of that is inferable from a prompt that says “add a dashboard.”

So the guides below start with authorization even though authentication comes first in the flow, because authorization is where generated apps actually break.


Choosing and designing: The decisions behind the login flow

Before any of the mechanics, three decisions set the shape of everything else: how users prove who they are, which method you accept, and what your permission model allows. They’re the expensive ones to change after you have users, and the ones generated code makes silently by default.


Authorization: Every object needs an owner check

The canonical failure is an endpoint like GET /invoices/42 that verifies a session and then fetches invoice 42. Any logged-in user can read any invoice by changing the number. This is broken access control, also called IDOR (insecure direct object reference), and it survives review because the code contains a real, working permission check — just the wrong one.


Login mechanics: Sessions, cookies, and tokens

Once a user proves who they are, something has to carry that proof across requests. That carrier is a session cookie, a token, or a signed assertion, and each has different properties around expiry, revocation, and exposure. The assistant picks one, usually the one most represented in its training data, which is not the same as the one that fits your app.


Credentials: Passwords, hashing, and the alternatives

Password handling is the one place where a mistake is unrecoverable rather than merely exploitable: if you store passwords in a form that can be reversed, a database leak becomes a credential leak for every user, including on other sites. Hashing with a per-user salt and a slow algorithm is the baseline, and it’s a decision the assistant makes for you in one line.


Federated login: OAuth and third-party identity

“Sign in with Google” is an OAuth flow, and the gap between a working integration and a correct one is mostly verification: confirming the token was issued for your client, checking the audience, and deciding what to do about an email that already exists in your database. Assistants wire up the happy path and leave the identity-linking edge cases for you to discover.


Session hygiene: Headers, policies, and expiry

A login is only as safe as the transport and the surrounding browser policy. Security headers do more work than most builders expect: they prevent content injection, restrict where a page can submit credentials, and force HTTPS. None of them are part of the feature the assistant was asked to build, which is why they’re usually absent.


Abuse controls: Rate limiting and account protection

Authentication endpoints are the ones attackers point bots at, because unlimited attempts make every other control moot. Rate limiting per IP and per account, plus a lockout or slowdown policy, is the difference between an unusable attack and thousands of successful ones.


Testing auth: Proving the checks exist

You can’t tell whether authorization works by reading one endpoint — the bug is always on the endpoint someone forgot. The practical approach is a short, repeated test: log in as user A, request user B’s resources by ID, and confirm a 403 or 404 for every one of them. Do it per feature, not once at the end.


Common mistakes: The twelve that keep showing up

Most auth failures in generated apps come from a short list of recurring defects — reversible password storage, tokens decoded but never verified, logout that doesn’t invalidate, reset links that never expire, MFA enforced in the UI rather than the session, and endpoints that authenticate without authorizing. They’re worth reading as a set, because each one passes a demo and a code review.


Where this bites vibecoders

The prompt was “add a projects page where users can see their projects,” and the assistant delivered exactly that — for the user who asked. The route checks the session, the query fetches by project ID, and the UI only ever links to the signed-in user’s projects, so clicking around the app confirms it works. What the code never encodes is the rule that a project belongs to a person: no ownership condition in the query, no role check on the update endpoint, no restriction on which fields a user may set. Every one of those is invisible in normal use and trivially exploitable by anyone who edits an ID in the URL, which is precisely the class of flaw scanners report as broken access control. When you accept auth code from an assistant, verify the negative case — what happens when a valid user requests something that isn’t theirs — because that’s the only case the generated code tends not to handle.

Checklist

  • Check ownership in the query, not just the route: fetch where ID and user match.
  • Test every user-specific endpoint with a second account before shipping the feature.
  • Hash passwords with a slow, salted algorithm — never store them reversibly.
  • Set session expiry, and make logout actually invalidate the session.
  • Put rate limits and lockouts on login, registration, and password reset.
  • Verify OAuth tokens were issued to your client before trusting them.
  • Add security headers to every response, especially on authenticated pages.
  • Never put signing keys or service credentials in front-end code.
  • Re-run the access-control test whenever a new endpoint is added.

FAQ

Why can users see each other’s data in the app my AI built?

Because the endpoint checks whether you’re logged in but not whether the record belongs to you. The assistant implements authentication (is there a valid session?) and skips authorization (does this user own record 42?). The fix is an ownership check on every query for a user-specific record, not just on the route.

Should I use sessions or JWTs?

Use server-side sessions for a normal web app with a browser front end — logout, revocation, and expiry are straightforward. Reach for JWTs when you have multiple services that need to verify a caller without a shared session store, and accept that revocation becomes your problem. Most small apps that adopt JWTs by default would be simpler with sessions.

Is OAuth the right way to add ‘Sign in with Google’?

Yes for the sign-in flow itself — it’s the standard. The mistake is treating the provider’s job as done once you receive the token. You still need to create or link a local account, decide what happens when the email changes, and check that the token was issued to your application and not to someone else’s.

Where should the authorization check live?

In the data access layer, next to the query, so the ownership condition is part of the fetch: select the record where the ID and the user match. Putting checks in the route handler or the UI means every new endpoint has to remember to call them, and the one that forgets is the vulnerability.


Sources

Share: