You logged in fine, but the screen shows other users' orders, profiles, or notes mixed in with yours. This is a data leak, so it's the highest-priority bug to fix. The cause is almost always the same: the server isn't filtering by "who owns this row" and is sending everything to the browser.

Does this match your symptom?

  • You're logged into your own account but the list contains other people's data
  • Changing the numeric id in the URL opens someone else's detail page
  • It was fine early on, but once you had more users they could see each other's data

Why it happens

Three common causes for non-developers:

  1. Supabase RLS (Row Level Security) is off — tables in the public schema are readable through the API in full unless RLS is enabled.
  2. The query has no "only mine" condition — a missing WHERE user_id = ..., or Firebase rules left in test mode (if true).
  3. Filtering only in React (client-side) — all rows, including other people's, already reached the browser and you just hid them on screen. Open the DevTools Network tab and they're all there. This is not security.

30-second diagnosis

In the browser: F12 → Network tab → click the request that loads the list → Response.

If the JSON response contains other users' rows, the server is sending them, so you must fix it on the server (below). Editing React alone will not fix it.

Fix A — If you use Supabase

  1. Make sure the table has a user_id column (add a uuid column holding the auth.users id if it's missing).
  2. Run this in the dashboard SQL Editor (replace todos with your table name):
sql

-- 1) Turn RLS on
alter table todos enable row level security;

-- 2) Auto-fill the owner id on new rows (optional)
alter table todos alter column user_id set default auth.uid();

-- 3) Read only my rows
create policy "select own rows" on todos
  for select to authenticated
  using ( auth.uid() = user_id );

-- 4) Insert only as myself
create policy "insert own rows" on todos
  for insert to authenticated
  with check ( auth.uid() = user_id );

-- 5) Update only my rows
create policy "update own rows" on todos
  for update to authenticated
  using ( auth.uid() = user_id )
  with check ( auth.uid() = user_id );

-- 6) Delete only my rows
create policy "delete own rows" on todos
  for delete to authenticated
  using ( auth.uid() = user_id );
  1. Save, log in as a different account, and retest. Other users' rows now drop out of the response entirely.

> Note: once RLS is on, nothing is visible until you add policies — that's expected. The policies above are what restore "only mine."

Fix B — If you use Firebase (Firestore)

In the Firebase console go to Firestore Database → Rules, replace the test-mode rule (allow read, write: if true;) with the owner rule, and Publish:

text

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /todos/{docId} {
      allow read, update, delete: if request.auth != null
                                  && request.auth.uid == resource.data.uid;
      allow create: if request.auth != null
                    && request.auth.uid == request.resource.data.uid;
    }
  }
}

You must also filter the query to your own data. Firestore rules are not filters: a broad query isn't silently trimmed — it's rejected outright.

js

import { collection, query, where, getDocs } from "firebase/firestore";

const q = query(
  collection(db, "todos"),
  where("uid", "==", auth.currentUser.uid)   // only mine
);
const snap = await getDocs(q);

(If your data is keyed by user id in the document path, use match /users/{userId} with allow read, write: if request.auth.uid == userId;.)

Fix C — If you have your own backend (Express, etc.)

Force the query with the userId taken from the login session/token. Never trust an id sent by the client — it can be swapped.

js

// use the value from the session only
const rows = await db.query(
  "select * from todos where user_id = $1",
  [req.user.id]   // NOT req.body / req.query id
);

The one line to remember

A client-side (React) filter is for tidying the screen, not for security. Only the server (RLS, security rules, WHERE) actually stops the leak.