-- pgTAP: auth.users role/aud defaulting (migration 013) -- Run with: psql -U postgres -d postgres -f supabase/tests/008_auth_user_role_default.sql -- -- Covers the bug found on ambrosio (2026-04-15): GoTrue v2.158.1 inserts -- OIDC-created users into auth.users with empty-string `role` and `aud`, -- which later makes PostgREST's `SET LOCAL role = ''` fail with -- `role "" does not exist`. Migration 013 adds a BEFORE INSERT trigger + -- backfills the existing rows. CREATE EXTENSION IF NOT EXISTS pgtap; BEGIN; SELECT plan(7); -- ── Existence assertions ──────────────────────────────────────────────── SELECT has_function( 'auth', 'ensure_user_role_default', ARRAY[]::text[], 'ensure_user_role_default trigger function exists in the auth schema' ); SELECT has_trigger( 'auth', 'users', 'ensure_role_default', 'BEFORE INSERT trigger ensure_role_default exists on auth.users' ); -- ── Backfill assertion ────────────────────────────────────────────────── SELECT ok( (SELECT count(*) FROM auth.users WHERE role IS NULL OR role = '' OR aud IS NULL OR aud = '') = 0, 'No pre-existing auth.users row has an empty role or aud' ); -- ── Trigger behavior ──────────────────────────────────────────────────── -- Insert a synthetic user with empty role/aud; the trigger should fill both. -- Drop the AFTER trigger locally so we don't exercise handle_new_user's -- side-effects (tested elsewhere, not the focus here). ALTER TABLE auth.users DISABLE TRIGGER on_auth_user_created; -- NEW row with empty strings INSERT INTO auth.users (instance_id, id, email, role, aud) VALUES ( '00000000-0000-0000-0000-000000000000', 'f0000000-0000-0000-0000-000000000001', 'empty-role@test.local', '', '' ); SELECT is( (SELECT role FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000001'), 'authenticated', 'Empty role on INSERT is defaulted to authenticated' ); SELECT is( (SELECT aud FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000001'), 'authenticated', 'Empty aud on INSERT is defaulted to authenticated' ); -- NEW row with NULL values INSERT INTO auth.users (instance_id, id, email, role, aud) VALUES ( '00000000-0000-0000-0000-000000000000', 'f0000000-0000-0000-0000-000000000002', 'null-role@test.local', NULL, NULL ); SELECT is( (SELECT role FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000002'), 'authenticated', 'NULL role on INSERT is defaulted to authenticated' ); -- Explicit non-empty role is preserved (trigger should only fill when empty). INSERT INTO auth.users (instance_id, id, email, role, aud) VALUES ( '00000000-0000-0000-0000-000000000000', 'f0000000-0000-0000-0000-000000000003', 'custom-role@test.local', 'service_role', 'authenticated' ); SELECT is( (SELECT role FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000003'), 'service_role', 'Explicit role is preserved by the trigger' ); SELECT * FROM finish(); ROLLBACK;