Supabase makes it easy to build a real app quickly.
That speed is exactly why the privacy boundary deserves a boring, explicit test before launch.
The question is not only:
Can Alice log in and see Alice's data?
That is the happy path.
The launch-risk question is:
Can Alice read Bob's data if she changes an id, route, org id, request, storage path, or realtime subscription?
If you cannot answer that with evidence, your app may still be a polished prototype rather than a shippable product.
RLS “on” is not the same as RLS proven
I see builders treat Row Level Security as a checkbox:
- RLS enabled
- auth added
- the UI filters by user
- the dashboard looks private
- ship it
But a private-looking UI can hide a weak data boundary.
Common examples:
- a policy allows any
authenticateduser, not the actual owner; - a React filter hides rows, but the database policy does not;
- a server route uses a trusted key without checking ownership;
- storage bucket rules are different from table rules;
- organization switching leaves stale access state;
- realtime channels broadcast rows the page itself would not show;
- admin/support paths work in development and accidentally remain reachable.
None of these require a dramatic hacker story. They are ordinary launch mistakes.
The smallest useful test: two users, two orgs, blocked cross-access
Before showing the app to real users, create the simplest negative test you can:
- Create Alice.
- Create Bob.
- Put Alice in org Alpha.
- Put Bob in org Beta.
- Create private rows for both.
- Log in as Alice.
- Try to read Bob's rows.
- Try to update Bob's rows.
- Try to delete or export Bob's rows.
- Try the same boundary through storage, server routes, RPC/functions, and realtime listeners.
Passing the happy path is not enough.
The useful evidence is blocked cross-access.
A practical pre-launch checklist
Before launch, I would want evidence for these checks:
- RLS is enabled on every private table, not just the main customer table.
- Policies prove ownership or membership using
auth.uid()and trusted org/team membership. - The client never exposes a service-role key.
- Storage buckets and file URLs are scoped to the same privacy boundary.
- Server routes check session, role, and ownership before reads and writes.
- Alice/Bob negative cases cover read, write, update, delete, export, and subscribe.
- Org/team switching cannot leave stale local access.
- Realtime channels follow the same ownership rules.
- Admin/support paths are explicit, logged, and unavailable to ordinary users.
- One written launch test records what was proven and what was not.
This is not a replacement for a professional security review.
It is a practical “do not accidentally ship the obvious leak” check for tiny SaaS apps, AI-built apps, and paid pilots.
Why this matters more for AI-built apps
AI helps people build faster than their review process can keep up.
That creates a weird failure mode:
- the interface looks credible;
- the demo works;
- the app has auth;
- the app has a database;
- the founder feels close to launch;
- but the boundary between users was never attacked.
When the app touches private prompts, documents, customer records, usage logs, generated outputs, or payment-linked features, this is not a small detail.
It is one of the boundaries that decides whether you can safely invite real people.
Free checker
I made a free no-login Supabase RLS/Auth Exposure Checker:
It asks the practical pre-launch question:
Could one user read another user's data?
Use it to generate a quick exposure report before launch.
If the flow still feels risky
If you want a second pass on one critical app flow, I also have a fixed-scope one-flow preflight:
AI App One-Flow Preflight — 48-Hour Evidence Review
One app, one critical flow, 10 focused checks, repro notes, and a smallest-fix-first action plan.
https://contra.com/s/r7s2Elvl-ai-app-one-flow-preflight-48-hour-evidence-review?r=eric_choi_td1brz18&utm_source=devto&utm_medium=article_cta&utm_campaign=supabase_rls_two_user_test
No giant audit. No vague “I can help.” One risky flow, checked deliberately.
Top comments (0)