Request the diagnostic

The Auth Check on the Wrong Side of the Network

bringforth · Rohit Chaudhri · Blog · March 21, 2026 · 7 min read

And somewhere in your Supabase dashboard, every row of their data is sitting unprotected — because your browser showed a login screen, and everyone, including the AI that built it, assumed that meant the database was locked.

Your demo worked. The investor leaned forward. Your users signed up. And somewhere in your Supabase dashboard, every row of their data is sitting unprotected — because your browser showed a login screen, and everyone, including the AI that built it, assumed that meant the database was locked. It wasn't. The auth check was on the wrong side of the network, and Supabase ships with Row Level Security turned off by default. The question isn't whether this applies to your app. The question is whether you've checked.

A working login screen in the browser does not protect your database — only server-enforced rules do, and AI tools don't set those up unless you tell them to.

What Does 'Client-Side Auth' Actually Mean — and Why Is It the Wrong Place?

Client-side auth means the lock lives in the browser, not the database. Your browser runs code your user controls; your database runs code your server controls. Checking credentials in the browser before loading a page is a reasonable UX choice — it is not a security boundary. Any request that skips the browser goes straight to the database, and the browser's lock never fires.

Think of it this way: bolting a padlock to a cardboard wall. The padlock is real. The wall is the problem. A login screen that redirects unauthenticated users away from a page is the padlock. The cardboard wall is a database that accepts any direct query, authenticated or not.

When a vibe-coded app routes all access through its front-end and trusts that route to enforce permission, it has placed the security perimeter in the least durable location possible. The database does not know or care that a user was supposed to log in first. It answers questions. Unless it is told — explicitly, at the row level — whose questions it is allowed to answer, it answers everyone's.

What Can Someone Actually Do to My App if the Auth Check Is Only in the Browser?

An attacker bypasses your browser UI entirely — no login page, no redirect, no token check — and sends a direct API call to your database. If Row Level Security is off, Supabase returns the data. Every record in that table: names, emails, payment references, messages, whatever your users trusted you with. The gate your legitimate users walk through never fires, because the attacker didn't use the gate.

This isn't theoretical. The attack requires no special tooling — just the public API endpoint that Supabase exposes for every project, which is findable from your app's network traffic in under a minute.

The consequences are not dramatic-movie dramatic. They are quietly catastrophic. A competitor pulls your entire user list. A bad actor modifies records without leaving obvious traces. Sensitive data leaves your database silently, before you have any reason to look at your logs. You find out weeks later, or you don't find out at all until a user tells you something is wrong.

As Ken Robinson put it: "Build a skyscraper on sand and it will fall over or have to be knocked down. Build it on a proven, stable foundation and it will be solid. AI gets you quick, but likely on sand. If you use it, make sure you really study what was built."

The exposure is not a function of your intent or your users' trust in you. It's a function of a default that ships quietly and a browser lock that was never designed to do the job everyone assumed it was doing.

Why Do AI-Built Apps Ship This Way by Default — Especially on Supabase?

Supabase creates every new table with Row Level Security disabled. AI coding tools generate queries that work against those tables — because they do work. The demo runs fine. The queries return data. Nothing breaks, nothing warns, nothing flags the gap. The AI saw a working system and stopped there.

This is the structural reason vibe-coded apps carry this specific vulnerability at scale. Not carelessness. Not a bad developer. A silent default that the AI never touches because the app functions perfectly without it at demo time.

Independent confirmation of this pattern is consistent across the builder community. Supabase tables created through AI-assisted workflows ship with RLS off by default, and the tools generating the queries have no mechanism to surface this — they optimize for a working result, not a secure one.

Adam Palicz, Founder of SpiniX, described the adjacent problem directly: "sometimes, even though Bolt has access to Supabase or Netlify, it can't complete the required config. the worst part? it doesn't tell you this. so you waste hours, burn credits, only to realize - you had to fix it manually elsewhere."

The silence is the problem. Not the tool. Tools that build for demos are not built to surface production security gaps — and Supabase's default makes this particular gap invisible until someone looks specifically for it.

Fraser Seymour framed the category issue clearly: "I'd like to see some indication of what architectural and technology choices these constructor apps make. I'd also like to see these tools able to accept guidance in these same domains. Prototypes and POCs are great but for an actual production codebase ignorance of the strategic elements is a nonstarter."

How Do I Tell if My Own App Has This Problem Right Now?

Open your Supabase dashboard, go to the Table Editor, select each table, and check whether Row Level Security shows as enabled. If any table your users' data lives in shows RLS as off, your database is unprotected at the row level — regardless of what your browser enforces. Then run a direct API call to a protected endpoint without a valid session token and check whether data returns.

This is a fifteen-minute diagnostic, not a full audit. Two checks, two answers.

First check: Supabase dashboard → Authentication → Policies. Any table without policies listed is exposed. The status is visible without writing a single line of code.

Second check: using your browser's developer tools or any HTTP client, make a raw GET request to your Supabase REST endpoint for a protected table — no Authorization header, no session. If rows come back, your auth is client-side only and the database does not enforce it.

Chris Nurse named the broader accountability gap: "Taking what you said literally, 25000 apps per day means every one needs to be carefully checked by someone who is not a developer, doesn't understand cyber security, compliance, load testing etc. So should it be 25000 prototypes?"

You don't need to be a security professional to run these two checks. You need fifteen minutes and the willingness to look.

What Does Fixing It Look Like — and How Long Does It Actually Take?

Enable Row Level Security on every Supabase table, write policies that enforce ownership at the row level, and move any remaining auth logic to server-side functions or middleware. A focused engineer closes the most critical gaps in two to three days — without touching your product's front-end behaviour or your users' experience.

The fix does not require rebuilding your app. It does not require changing your UI, your flows, or your onboarding. Your users see nothing different. What changes is where the enforcement happens: out of the browser, into the database, where it should have been from the first query.

RLS policies in Supabase are SQL expressions attached to each table. A policy that reads auth.uid() = user_id means the database itself checks whether the requesting user owns the row before returning it. No browser required. No redirect logic required. The check happens before the data moves.

Two to three days. No rebuild. The front-end your users already know stays exactly as it is — the cardboard wall gets replaced with something that can hold weight.

What Now?

Open your Supabase dashboard today. Check RLS status on every table. Run one unauthenticated API call. If data comes back, you have a two-to-three-day fix ahead of you — not a crisis, not a rebuild. A specific problem with a specific remedy. Start there.