---
name: plurabase-auth-rls-app
description: Build per-user data on a Plurabase private-pilot project - a table with Row Level Security, an Edge Function that acts as the caller, and a check with two users through supabase-js.
---

# Per-user data with Auth, RLS and an Edge Function

> Status: Available in pilot (database.rest)

> Status: Available in pilot (auth.email-password)

> Status: Available in pilot (functions.edge)

Use this when each signed-in user of the application must see and change only their own rows. Connect first with the `plurabase-mcp-connect` skill using a **Develop** token. Facts: https://plurabase.com/docs/facts/

## Steps

1. Apply a migration with `apply_migration` (fresh `operation_id`), for example `notes_table`:

```sql
create table public.notes (
  id bigint generated always as identity primary key,
  owner_id uuid not null default auth.uid(),
  body text not null,
  created_at timestamptz not null default now()
);
alter table public.notes enable row level security;

create policy notes_select on public.notes for select to authenticated using (owner_id = auth.uid());
create policy notes_insert on public.notes for insert to authenticated with check (owner_id = auth.uid());
create policy notes_update on public.notes for update to authenticated
  using (owner_id = auth.uid()) with check (owner_id = auth.uid());
create policy notes_delete on public.notes for delete to authenticated using (owner_id = auth.uid());
```

2. Check the result with `list_tables` (RLS enabled) and `get_policies` (four policies).
3. Deploy a function with `deploy_edge_function` and `expected_revision: "absent"`. Inside it, create the client with the project's publishable key and the caller's `Authorization` header so RLS applies to the caller; never use a service-role key. Only local files and pinned versions of `@supabase/supabase-js` v2 and `@supabase/server` can be imported.
4. Verify from a client with `@supabase/supabase-js` v2, the API URL from `get_project_url` and the key from `get_publishable_keys`: sign up two test users, insert a row as each, and confirm that user A cannot read, update or delete user B's row and that an anonymous request reads nothing.

## Guardrails

- Never put a service-role or `sb_secret_` key in frontend code or in a function that acts for a user.
- `execute_sql` bypasses RLS; it cannot prove that policies work.
- Do not insert rows into `auth.users` directly; create users through Auth sign-up.
- Confirmation emails to application users are not delivered outside the pilot host. Use test accounts the operator can confirm, or ask the operator how sign-up confirmation is set up for the project.
