-- Migration 013: default `role` and `aud` to 'authenticated' on auth.users. -- -- Background: GoTrue v2.158.1, when creating a user via the OIDC path -- (Keycloak external provider), persists the user with empty-string `role` -- and `aud` columns. The JWT it subsequently issues carries the empty role, -- and PostgREST's per-request `SET LOCAL role = $role_claim` then fails with -- `role "" does not exist` on every REST call — including RPCs. -- -- The seed users in `supabase/seed.sql` hardcode role='authenticated' and -- aud='authenticated', which is why dev never surfaced this. Prod (ambrosio) -- hit it on the first real self-registration. -- -- Two parts: -- 1. Backfill any already-persisted rows missing role/aud. -- 2. BEFORE INSERT trigger that fills empty/NULL role + aud with -- 'authenticated' so future self-registrations are safe without -- requiring a GoTrue upgrade. -- ── 1. Backfill ────────────────────────────────────────────────────────── UPDATE auth.users SET role = COALESCE(NULLIF(role, ''), 'authenticated'), aud = COALESCE(NULLIF(aud, ''), 'authenticated') WHERE role IS NULL OR role = '' OR aud IS NULL OR aud = ''; -- ── 2. Trigger function + trigger ──────────────────────────────────────── CREATE OR REPLACE FUNCTION auth.ensure_user_role_default() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.role IS NULL OR NEW.role = '' THEN NEW.role := 'authenticated'; END IF; IF NEW.aud IS NULL OR NEW.aud = '' THEN NEW.aud := 'authenticated'; END IF; RETURN NEW; END; $$; DROP TRIGGER IF EXISTS ensure_role_default ON auth.users; CREATE TRIGGER ensure_role_default BEFORE INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION auth.ensure_user_role_default();