Skip to content

Guides · VibeVigil

Can One User See Another User's Data? How to Test Your Lovable, Bolt, v0 or Cursor App

A simple two-account test to check if users of your AI-built app can read or change each other's data, through Supabase, your API routes or file uploads, plus a fix prompt for your coding agent.

Your app has sign-up and login. Each user has their own notes, orders, bookings or messages. The screen only shows your stuff when you log in, so it feels safe.

But the screen is not the lock. What matters is what your database and API hand out when someone asks for it directly. This guide shows you how to test that yourself, with two test accounts and your browser. You don't need to be a developer.

Only run these tests on an app you own. Testing someone else's app without permission is not okay, and in many places it's illegal.

The short answer

  • Make two test accounts (User A and User B).
  • As User B, try to read, change and delete User A's data: by changing IDs in the URL, by replaying requests, and by calling Supabase directly.
  • If any of that works, the fix is on the server side: Row Level Security (RLS) policies tied to auth.uid(), and API routes that take the user from the login session, never from the request.
  • Hiding a button or a page in the UI is not a fix.

Why this happens so often in AI-built apps

AI tools are great at building the screens. They are less reliable at the access rules behind them. Common gaps:

  • A Supabase table with RLS off, or a policy that is just true, so any logged-in user (or anyone with the public key) can read every row.
  • A policy that checks "is someone logged in?" instead of "is this row theirs?"
  • An API route or edge function that reads userId from the URL or request body and trusts it.
  • Pages that are only hidden in the menu, like /admin, with no check on the server.

Security folks call this "broken object level authorization". It is the first item on the OWASP API Security Top 10, because it's common and easy to abuse.

Before you start

  1. Use your live app or a staging copy with the same database rules.
  2. Make User A and User B with two different emails.
  3. Log in as User A and create some data: a note, an order, a profile photo, whatever your app stores. Write "USER A SECRET" in it so it's easy to spot.
  4. Use two browsers (or one normal window and one private window), so you can stay logged in as both.

Test 1: Change the ID in the URL

  1. As User A, open one of your items. Look at the address bar. You may see something like /notes/123 or /orders/8f2c....
  2. Copy that URL.
  3. Paste it into User B's browser.

Good: User B gets "not found", "no access", or an empty page. Bad: User B sees "USER A SECRET".

Also try nearby numbers (/notes/122, /notes/124). Long random IDs make guessing harder, but they are not a lock. IDs leak through shared links, emails and logs.

Test 2: Replay a request as the other user

The screen can hide data that the API still sends. So look at what the API sends.

  1. As User B, open your browser's developer tools (right-click, then Inspect) and go to the Network tab.
  2. Use the app normally. Click into User B's own items.
  3. Look at the requests. Click one and check the Response. Ask yourself: is there any data in here that isn't User B's? Other users' emails, names or rows are a red flag, even if the screen doesn't show them.
  4. Find a request that includes an ID (in the URL or the body). Right-click it and choose Copy as fetch. Paste it into the Console, swap User B's ID for User A's ID, and press Enter.

Good: an error (like 401, 403 or 404) or an empty list. Bad: User A's data comes back, or the change goes through.

Try this for reading, editing and deleting. Some apps lock down reads but forget updates and deletes. Run the delete test only on test data.

Test 3: Call Supabase directly with the public key

If your app uses Supabase, the URL and the public anon (or publishable) key are in your site's code. That's normal and expected. Supabase is designed for that key to be public, as long as RLS protects your tables. (Supabase: Row Level Security)

So test what that key can reach:

  1. In the Network tab, find a request to your-project.supabase.co/rest/v1/....
  2. Copy it as fetch, and run it in the Console of a private window where you are logged out.
  3. Then run it again while logged in as User B, but asking for User A's rows (for example, remove the filter on user_id).

Good: logged out, you get an error or nothing. As User B, you only get User B's rows. Bad: you get rows that belong to someone else.

If you hit a 42501 "permission denied for table" error on a new table, that's a separate change Supabase made on October 30, 2026. See our guide on fixing error 42501 safely. Don't "fix" it by granting everything to everyone.

Test 4: Files and uploads

Profile photos, PDFs and receipts often live in Supabase Storage or a similar bucket.

  1. As User A, upload a file. Open it and copy its URL.
  2. Open that URL in User B's browser, and in a logged-out private window.

Good: private files fail to load for anyone but User A (or load only through a short-lived signed link). Bad: the file opens for anyone with the link.

More on this in Is My Supabase Storage Bucket Public?

Test 5: Admin and paid features

  1. As User B (a normal, free user), type your admin URL straight into the address bar, like /admin or /dashboard/admin.
  2. If your app has paid features, try opening a paid page or calling a paid feature's API as a free user.
  3. If your users table has columns like role, is_admin, is_pro or plan, check whether User B can change their own value through the API (Test 2 style).

Good: the server says no. Bad: the page loads, or User B can make themselves an admin or a paying user.

What to do if a test fails

The fix belongs on the server, not in the UI. In plain words:

  • Turn on RLS for every table in the public schema, and give each table policies that compare the row's owner column to auth.uid(). Do this for SELECT, INSERT, UPDATE and DELETE.
  • Never trust a user ID sent by the browser. API routes and edge functions should read the user from the verified login session (the JWT), then check that the user owns the thing they're asking for.
  • Keep the service role key on the server only. It skips RLS completely.
  • Lock columns that grant power (role, plan, admin flags) so only your server can change them.
  • Re-run the tests after every fix, and again after any big AI change or rebuild.

Copy-paste prompt for your coding agent

Audit my app for users being able to see or change each other's data. Assume the Supabase anon key is public and an attacker has a normal free account.
1. List every table in the public schema, whether RLS is on, and every policy in plain words. Flag any policy that is just true or only checks that a user is logged in. Rewrite them so users can only select, insert, update and delete rows where the owner column equals auth.uid().
2. List every API route, server action and edge function that reads or writes user data. For each one, confirm it gets the user from the verified session, not from the URL or request body, and checks ownership before acting. Fix any that don't.
3. Check every view and every security definer function. Make sure none of them lets one user read another user's rows.
4. Make sure columns like role, is_admin, plan or is_pro can only be changed by the server.
5. Make sure private storage buckets are not public and use owner-only policies or signed URLs.
6. Make sure the service role key is only used in server code and never in any NEXT_PUBLIC_ or VITE_ variable.
Show me each change before you make it and explain in plain words what an attacker could do today.

Make it a habit

These tests take less time the second time. Run them:

  • before you launch,
  • after any big feature or AI "rebuild",
  • whenever you add a new table or a new kind of upload.

A scan checks what it can see from the outside. It can't promise an app is fully secure, so keep doing the two-account test too.

Sources

Scan your app free. It checks the public page of a site you own. No account.

Can One User See Another User's Data? How to Test Your Lovable, Bolt, v0 or Cursor App · VibeVigil