Skip to content

Commit 776ee2b

Browse files
John Kealyclaude
andcommitted
Grant table privileges to the API roles in the initial migration
RLS policies only filter rows on top of table-level privileges — Postgres checks the table ACL first and rejects with 42501 before RLS is ever consulted. Supabase's stock default privileges normally paper over this by auto-granting DML to the API roles, but that's a database setting that can be hardened away, and then every query from the app dies at the gate. authenticated gets SELECT/UPDATE/DELETE on users (inserts happen via the SECURITY DEFINER trigger on auth.users) and full DML on devices; service_role gets full DML on both (it bypasses RLS, not the ACL). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 61bb0a9 commit 776ee2b

1 file changed

Lines changed: 23 additions & 0 deletions

File tree

‎supabase/migrations/20260101120000_initial_schema.sql‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,29 @@ CREATE POLICY "Users can delete their own devices"
104104
TO authenticated
105105
USING (auth.uid() = user_id);
106106

107+
-- ============================================================================
108+
-- GRANTS FOR API ROLES
109+
-- ============================================================================
110+
-- RLS policies only filter rows on top of table-level privileges — they are
111+
-- not permissions themselves. Postgres checks the table ACL first and rejects
112+
-- queries with "permission denied" (42501) before RLS is even evaluated.
113+
-- Supabase's stock default privileges usually grant DML to the API roles
114+
-- automatically, but that's a database setting that can be (and sometimes is)
115+
-- hardened away, so this migration grants what it needs explicitly. Any future
116+
-- table needs its own GRANTs alongside its policies.
117+
118+
-- users: policies exist for SELECT / UPDATE / DELETE (inserts happen via the
119+
-- SECURITY DEFINER trigger on auth.users, so authenticated needs no INSERT).
120+
GRANT SELECT, UPDATE, DELETE ON public.users TO authenticated;
121+
122+
-- devices: the app upserts (SELECT + INSERT + UPDATE) and deletes push tokens.
123+
GRANT SELECT, INSERT, UPDATE, DELETE ON public.devices TO authenticated;
124+
125+
-- The backend connects as service_role, which bypasses RLS but still requires
126+
-- table-level privileges.
127+
GRANT SELECT, INSERT, UPDATE, DELETE ON public.users TO service_role;
128+
GRANT SELECT, INSERT, UPDATE, DELETE ON public.devices TO service_role;
129+
107130
-- ============================================================================
108131
-- FUNCTIONS
109132
-- ============================================================================

0 commit comments

Comments
 (0)