Security

Vibe Coding Authentication Security: When the UI Is Your Only Wall

8 min read By Red Corner

Your app has an admin panel that normal users never see, because the AI hid the button behind a role check. You have accounts, login, and a dashboard that shows each person only their own data. Then someone opens developer tools, copies the request your admin page makes, changes one ID, and reads another customer’s records. Nothing on your server ever asked who was calling.

This is one of the most common vibe coding authentication security failures there is, and it is nearly invisible from the inside because the app works perfectly. A builder whose team cleans up AI-built apps says the same handful of problems appear in almost every codebase they open, and this one is near the top every time.

What this problem looks like

The pattern that shows up again and again in builder forums has one skeleton: the frontend decides what you may do, and the backend takes its word for it.

The first face is the hidden button. You ask for an admin-only delete feature; the AI hides the button from non-admins and adds an API route that performs the delete. The route itself checks nothing, so anyone who finds it with developer tools can delete whatever they like.

The second face is the ID swap. Your app fetches an invoice or profile by ID, and because the frontend only ever requests your own IDs, it never occurs to you to try someone else’s. The cleanup team suggests a two-minute test: sign up twice, add something under the first account, then request that record’s ID while logged in as the second. They say the request succeeds far more often than anyone expects.

The third face is specific to Supabase, which many vibe coded apps use for database and login. Supabase lets the browser talk to the database directly, and the only thing between a logged-in user and everyone else’s rows is row-level security, per-table rules saying which rows a user may read or change. When those rules are missing or too loose, your public key plus a login can query the whole table. One builder on that stack noted his AI tool kept changing code he had not asked it to touch, and security policies are as exposed to that as any other file.

All three are cousins of secrets and API keys exposed in vibe coded apps: the model has no picture of where the trust boundary sits.

Why the backend trusts the frontend when you vibe code

This is not random carelessness; it is a predictable consequence of how the tools work. You describe features in terms of what the user sees: “only admins should be able to delete posts.” The cheapest literal satisfaction of that sentence is to hide the delete control, and when you click around it looks right. Nobody typed “and the server must verify the caller’s role on every request,” so it was never built. The tool optimizes for the path a well-behaved user takes; an attacker never walks that path.

One commenter’s theory: models have seen enormous amounts of public code, much of it bad and annotated with why, so they are excellent at spotting an authorization hole in someone else’s code yet reproduce it when writing their own. That asymmetry tells you how to use the model to fix the problem.

Third, the model has no runtime. A veteran penetration tester asked the forum: after the agent wrote a new endpoint and the tests passed, did anything check whether it was exploitable on the running app? For most vibe coded apps the answer is no. As one poster put it, the starting assumption for AI-generated code should be that it contains security flaws, and the job is to prove otherwise.

How to fix vibe coded app authorization in Claude Code today

The cleanup team’s view is that a rewrite is unnecessary; in their experience the fixes take days rather than months.

Step one: inventory every endpoint, then add a server-side authorization check to each

Open Claude Code and ask for a list rather than a fix: “List every API route, server action and edge function. For each, say whether it verifies the caller is logged in and whether it verifies the caller may touch that specific record.” Cursor, Lovable and Replit can do the equivalent. Any row answering “no” or “only in the UI” is a hole.

Fix them one route at a time, stating the rule as a rule: “Verify the session server-side, load the record, confirm it belongs to the current user or that their role permits the action, and return a 403 otherwise. Never trust a role or user ID sent from the client.” A 403 is the response meaning “logged in, but not permitted.” Use plan mode to read what Claude Code intends before it changes anything, and commit after every route so a bad change costs one step back, not an afternoon.

Step two: verify Supabase row-level security

If you use Supabase this is not optional. For every table holding user data, confirm RLS is enabled with a policy for each of select, insert, update and delete. Ask Claude Code to print every table and its policies, then read them yourself; a policy that resolves to “true” for everyone is what you are hunting. Then run the two-account test: sign up twice, create something as account one, and request it by ID as account two, through the UI and by editing the request in the browser’s network tab. If it comes back, you are not done.

Step three: freeze the auth layer in CLAUDE.md

One commenter with a working process put it well: have someone competent get auth and permissions right once, then treat that part of the code as off-limits to agents from then on. In Claude Code the vehicle is CLAUDE.md, the project instructions the tool reads at the start of every session. Add rules in plain sentences:

  • Every route and server action must verify the session and check ownership or role on the server. Client-side role checks are for display only and never count as security.
  • Any new table must have row-level security enabled, with policies for all four operations, before the feature is done.
  • Any feature that fetches a record by ID must include a test that fetches it as a different user and expects a 403.

Cursor’s rules files and the project instructions in Lovable and Replit serve the same purpose.

Step four: use the model as a hostile reviewer, in a fresh session

Because models find these holes more reliably than they avoid them, run a separate review. Open a new Claude Code session with no memory of the build, or a sub-agent, and ask: “Assume this app is deployed. For each endpoint, describe how an authenticated user could read or modify data they do not own. Report, do not fix.” The fresh session matters because the model that wrote the code will defend it. The full exercise is in how to actually audit a vibe coded app’s security.

How a Red Corner CTO would have prevented this problem

The point of a CTO in your corner is that this class of bug never gets the chance to exist, because the rules that prevent it are set before the first prompt.

Before the first prompt, in week one, a CTO would have spent an hour drawing the trust boundary with you: the browser, which you never trust; the server, the only place permission decisions are made; the database, which enforces its own rules even when the server forgets. That picture becomes the first section of your CLAUDE.md. They would have insisted the auth and data-access layer be built first, reviewed by a human, then frozen, so every feature you vibe code afterward sits on top of it.

During the build, a CTO reviewing your code periodically would have caught the hidden-button admin route in the first review, because it is the first thing an experienced reviewer looks for. They would have asked one question of every new endpoint: what stops a logged-in stranger from calling this with someone else’s ID? And they would have turned the two-account test into an automated test so a later AI session cannot quietly undo it.

Before launch, they would have run the hostile review themselves and walked your Supabase tables policy by policy, looking for anything that amounts to “allow all.” They would have made sure login and password reset are rate limited, and would not have let you accept payments until the server owned the price and the webhooks were verified, because payments and user data carry their own exposure.

After launch, they would have set up error tracking and access logging so that one account fetching hundreds of IDs it does not own shows up as an alert rather than a complaint, and scheduled a re-review after every significant feature, because the danger with AI tools is not the code they write on day one but the code they rewrite on day forty.

Frequently asked questions

Is hiding admin buttons in the frontend enough security?

No. Hiding a control only changes what the browser draws, not the requests it can send; anyone can find the route the admin page calls and call it directly. Every permission decision has to happen on the server, or in the database through row-level security, on every request.

Does Supabase row-level security protect my app automatically?

Only if it is enabled on each table with correct policies for select, insert, update and delete. A table with RLS off is readable by anyone holding your public key and a login. List every table and its policies, read them yourself, then test with two accounts.

How do I vibe code with Claude Code securely from the start?

Write the trust rules into CLAUDE.md before building features: server-side checks on every route, RLS on every table, no client-supplied roles or user IDs. Build and freeze the auth layer first, then run a hostile review in a separate session before each release. Having someone with engineering judgment read the auth code once is worth more than any prompt.

Get a CTO in your corner

You do not need a computer science degree to ship a secure app. You need someone in the room who knows where the trust boundary is and checks that your tools respected it. That is Red Corner: real CTOs who answer your questions, review your code and guide you to launch. Get a CTO in your corner at redcorner.io.

Talk to your CTO before you commit to anything.

Every member starts with a call. We make sure we can help you, and that you are ready for the help.

Request a call with your CTO

Every member starts with a call. No card.