A Supabase RLS policy is not proven just because the dashboard looks correct. The real test is whether User A can reach User B's data through the UI, direct API calls, storage paths, RPC functions, Edge Functions, realtime subscriptions, or any backend route that uses elevated access.
If you are launching an AI-built SaaS app, the minimum RLS launch test is simple: create two real users, create private data for both, then try to make each user read, update, delete, list, download, or overwrite the other user's records.
Launch rule: do not ship private Supabase data until User B fails to access User A's rows through the browser, Supabase API, storage, RPC, Edge Functions, and your own backend routes.
Why do Supabase RLS tests matter before launch?
Supabase lets browser apps access the database safely when Row Level Security is enabled and policies are correct. The official RLS docs say RLS should be enabled on tables in exposed schemas, including the default public schema. They also explain that after RLS is enabled, no data is accessible through the API using a publishable key until policies allow it.
That secure default is useful, but it does not prove your product logic is correct. A policy can compile and still allow the wrong tenant, team member, workspace, or user to read data. That is why a launch audit needs behavior tests, not only a policy list.
What is the User A / User B RLS test?
The User A / User B test is a practical isolation check. User A owns one set of data. User B owns another set of data. The test proves that each user can only access the data they should see.
- Create User A with a normal customer account.
- Create User B with a second normal customer account.
- Create records for User A in every private table.
- Create records for User B in the same tables.
- Login as User B and try to access User A's data.
- Repeat reads, inserts, updates, deletes, file downloads, and direct API calls.
If User B can access User A's private rows by guessing an ID, changing a URL, editing a request body, or calling Supabase directly, the app is not ready to launch.
Which tables should be tested?
Test every table that stores private or account-specific data. Do not only test the obvious table that powers the dashboard. AI-built apps often create supporting tables during feature work, and those tables can be less protected than the main entity.
profiles,users,accounts, andworkspaces.- Documents, chats, messages, forms, leads, and generated reports.
- Invoices, subscriptions, usage records, credits, and entitlements.
- Teams, memberships, roles, invites, and organization settings.
- Files, exports, uploads, images, transcripts, and storage metadata.
- Automation logs, webhook events, CRM sync state, and background jobs.
How do I test SELECT policies?
Start with reads because read leaks are the easiest to miss. The UI may only show the current user's rows, but the API may still return another user's row when you request it directly.
- Login as User A and create a private record.
- Copy the record ID.
- Login as User B in another browser or private window.
- Try to load User A's record from the UI by changing the URL.
- Try a direct Supabase API request for that row using User B's session.
- Try list endpoints with filters removed or changed.
A correct private SELECT policy should return no row to User B. A broad policy such as USING (true) may be correct for public content, but it is a launch blocker for private tables.
How do I test INSERT policies?
Supabase policies for inserts use WITH CHECK. The important question is whether User B can create a row that belongs to User A, another workspace, or a paid account they do not control.
- Login as User B.
- Send an insert request with
user_id,owner_id,workspace_id, ororganization_idset to User A's values. - Check whether the row is rejected.
- Repeat for team-owned records, invite records, billing records, and generated content.
If the insert succeeds, User B may be able to poison another user's account, create records inside another workspace, or trigger downstream automation under the wrong owner.
How do I test UPDATE policies?
Update policies need special attention because they can check both the existing row and the new row. Supabase's docs explain that update policies can combine USING and WITH CHECK: one checks whether the row can be updated, the other checks whether the new row is allowed after the update.
Before launch, test both paths:
- User B cannot update User A's existing row.
- User B cannot update their own row and change ownership to User A, another workspace, or another organization.
- User B cannot upgrade their role, plan, credits, entitlement, or admin status through a direct update.
- User B cannot edit another user's invite, membership, file metadata, or billing state.
How do I test DELETE policies?
Delete tests are simple and important. User B should not be able to delete User A's records, files, team membership, invoices, usage logs, or automation jobs. Test deletes from the UI and direct API calls.
For each private table, ask: if this row disappears, can the user lose work, lose access, lose audit history, or corrupt billing state? If yes, test delete isolation before launch.
How do I test Supabase Storage RLS?
Storage needs separate tests. Table RLS does not automatically prove that private files are safe. User A / User B tests should cover listing, uploading, downloading, replacing, and deleting files.
- User A uploads a private file.
- User B tries to list the bucket or folder.
- User B tries to download the file by path.
- User B tries to overwrite the same path.
- User B tries to delete the file.
- Signed URLs expire and cannot be guessed from public paths.
Do RPC functions and Edge Functions need RLS tests?
Yes. RPC functions and Edge Functions can become a shortcut around otherwise good table policies. This is especially common when a backend function uses a secret key or legacy service role key to make a feature work.
Test every function that returns user data, creates records, changes memberships, updates billing state, processes files, or triggers AI jobs. A function can use elevated access internally, but it must still verify who is calling it and which user, workspace, or organization they are allowed to act on.
Can automated tests prove Supabase RLS?
Automated tests help a lot, but only when they model real product scenarios. Supabase documents database testing and linting through the CLI, and Postgres ecosystems often use pgTAP-style tests. Those tools are useful for repeatable checks, but a test with weak seed data can pass while production isolation is still broken.
Good RLS tests need:
- At least two normal users.
- Rows for both users in every private table.
- Tenant, workspace, and role variations if the app has teams.
- Negative tests that try forbidden reads and writes.
- Storage and backend function tests, not only table selects.
What should fail in a good RLS test?
A good RLS test is not only checking successful access. It should prove that forbidden access fails.
- User B cannot read User A's private rows.
- User B cannot insert rows owned by User A.
- User B cannot update User A's rows.
- User B cannot move their own row into User A's workspace.
- User B cannot delete User A's rows.
- User B cannot list, download, overwrite, or delete User A's files.
- Logged-out visitors cannot access authenticated-only data.
- Canceled or unpaid users cannot trigger paid workspace actions through backend routes.
Supabase RLS tests before launch
- Create User A and User B as normal app users.
- Create private rows for both users in every private table.
- Test direct row reads by ID.
- Test list queries with filters removed or changed.
- Test insert ownership with forged
user_id,workspace_id, ororganization_id. - Test update ownership and role changes.
- Test deletes on private records.
- Test storage list, download, upload, overwrite, and delete paths.
- Test RPC functions, Edge Functions, and backend routes.
- Test unauthenticated access where
auth.uid()is null. - Test team membership, admin roles, invites, and billing state.
- Repeat after schema, policy, storage, or backend route changes.
Launch verdict
Block ship if User B can read, update, delete, list, download, or overwrite User A's private data.
Ship with fixes if table RLS passes but storage, RPC, Edge Functions, or backend routes have not been tested yet.
Clear to ship when User A / User B isolation passes for tables, storage, functions, backend routes, logged-out requests, team roles, and billing-related data.
Useful references
- Supabase docs: Row Level Security
- Supabase docs: Testing and linting
- Supabase docs: Storage access control
Want User A / User B isolation checked before launch?
I test Supabase RLS, storage policies, service role paths, Edge Functions, backend routes, and cross-user data access in AI-built SaaS apps.
Book $299 Launch Audit View sample report