To test Supabase Row Level Security (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.

What you need

  • Your own app, live or on staging with the same database rules.
  • Two test accounts you create: A and B.
  • Two browsers, or one normal and one private window.

Step 1: Create data as user A

Sign in as A and create one of everything your app stores: a project, a note, an upload. Open DevTools > Network, reload, and find the request to /rest/v1/.... Note the table and the row id.

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

  • Change the ID in the URL. Open A's page address with A's id. B should get an empty page or an error.
  • Replay the request. Right-click a similar /rest/v1/ request > Copy as fetch, paste it in the Console and change the filter to id=eq.<A's id>. B should get [].
  • Ask for everything. Replay without a user filter. B should only see B's rows.

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

Replay an update or insert with A's user_id in the body. It should fail or change 0 rows. If B can insert a row with A's user_id, the insert policy is missing with check (auth.uid() = user_id).

Step 4: Repeat logged out

Your public key is in the page, so a stranger can send the same request. Private tables should return [] or a permission error.

Step 5: The SQL editor way (optional)

The SQL editor normally bypasses RLS, so a plain select proves nothing. Impersonate a user and roll 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; 

Step 6: What RLS doesn't cover by itself

  • Views run as their owner unless created with security_invoker = true.
  • security definer functions run with their owner's rights. Call them as B with A's id.
  • Storage has its own policies. As B, open A's file URL. Private files should need a signed URL.

If a test fails

Paste this into your AI coder:

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, 2) rewrite them so each user can only select, insert, update and delete rows where user_id = auth.uid(), with a matching WITH CHECK, 3) set views to security_invoker = true and check auth.uid() inside any security definer function, 4) give me the SQL and the two-account test to confirm the fix. Don't run anything until I say so.

Only test apps you own, with test accounts you created.

Free pre-launch checklist (14 checks to run before you share your link): https://milokits.com/checklist?ref=devto

We make Milo's Launch Check, a passive outside-in check for AI-built apps. It's launch week, so the Quick Check is $19 instead of $29 until 1 November (code QUICK19): https://milokits.com/?ref=devto#pricing