Milo Kits
Guide

How to test Supabase RLS with two accounts

To test Supabase RLS, create two test users, add data as user A, then try to read and change it as user B and as a logged-out visitor. If B or a stranger gets A's rows, your policies are too loose. If A can't see their own rows, a policy is missing. You can do it in the browser with the network tab, or in the SQL editor by impersonating each user inside a transaction you roll back.

What you need

Step 1: Create data as user A

Sign in as A and create one of everything your app stores: a project, a note, an order, an uploaded file. Open DevTools → Network, reload a page that shows A's data, and find the request to /rest/v1/…. Note the table name and the row id.

Step 2: Try to read A's data as user B

Sign in as B in the other browser. Then try three things:

  1. Change the ID in the URL. Open A's page address and swap in A's id. B should get an empty page or an error.
  2. Replay the request. In B's Network tab, find a similar /rest/v1/ request, right-click → Copy as fetch, paste it into the Console, and change the filter to A's id (for example id=eq.<A's id>). B should get [].
  3. Ask for everything. Replay a request without any filter on the user. B should only ever see B's own rows.

If B sees any of A's rows, the table's SELECT policy is too loose.

Step 3: Try to change A's data as user B

Still as B, replay an update or insert request with A's id or A's user_id in the body. A correct setup returns an error or changes 0 rows. If B can insert a row with A's user_id, the insert policy is missing a with check (auth.uid() = user_id).

Step 4: Repeat logged out

Sign out (or use a fresh private window) and replay the same read request. Your public key is still in the page, so a stranger can send it too. Private tables should return [] or a permission error.

Step 5: Check the SQL editor way (optional)

The SQL editor normally bypasses RLS, so a plain select there proves nothing. Impersonate a user inside a transaction and roll it back:

begin; set local role authenticated; set local request.jwt.claims = '{"sub": "USER_B_UUID", "role": "authenticated"}'; select * from public.your_table where user_id = 'USER_A_UUID'; -- expect 0 rows rollback;

Find the user UUIDs under Authentication → Users. For the logged-out test, use set local role anon; instead and skip the claims line.

Step 6: Test the parts RLS doesn't cover by itself

If a test fails

Fix prompt
I tested my Supabase app with two users. User B could [read / change] user A's rows in [TABLE / VIEW / FUNCTION / BUCKET]. Please: 1) show me the current policies for it, 2) rewrite them so each user can only select, insert, update and delete rows where user_id = auth.uid(), with a matching WITH CHECK on insert and update, 3) set views to security_invoker = true and check auth.uid() inside any security definer function, 4) give me the SQL plus the exact two-account test to confirm the fix. Don't run anything until I say so.

Write your rule in plain English first ("users can only see their own orders"), then compare it with what the policy actually says. Most mistakes show up there.

FAQ

Why test with two accounts instead of one?

With one account everything looks fine, because you're the owner of everything you see. Most RLS mistakes only show up when a different signed-in user asks for rows that aren't theirs, or when a logged-out visitor uses the public key.

RLS is on. Why do I need to test it?

RLS on with a broad policy such as using (true), or a policy that checks the wrong column, still lets other users in. RLS on with no policy blocks everyone, including the owner. Testing shows which one you have.

Can I test RLS in the Supabase SQL editor?

Yes. The SQL editor normally runs as a role that bypasses RLS, so switch role first: inside a transaction, set the role to authenticated and set request.jwt.claims to the test user's id, run your query, then roll back.

Does RLS protect views, functions and storage too?

Not automatically. Views run as their owner unless created with security_invoker, security definer functions run with their owner's rights, and storage has its own policies on storage.objects. Test each one.

Can I test another company's Supabase app this way?

No. Only test apps you own or are allowed to test, with test accounts you created. Don't read or change real users' data.

Sources

This guide is general information, not a security audit. Only test apps you own or have permission to test. Platforms change, so check the linked docs for current details. Spotted something out of date? Email hello@milokits.com.