From bcecdbb184d46fcb2832d2abc2368fb78818785c Mon Sep 17 00:00:00 2001 From: Lak-G Date: Mon, 24 Aug 2026 09:36:16 -0700 Subject: [PATCH] Fix: self-heal del profile en middleware + backfill de users existentes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El trigger prestamos_on_auth_user_created coexiste con otro trigger del proyecto vecino en auth.users y no siempre dispara. Consecuencia: usuarios que hacían login vía OAuth entraban con user pero sin profile, y la app los trataba como "no logueado". - Middleware: si hay user y no hay profile (email @uabc.edu.mx), upsert del profile con datos del OAuth (nombre desde user_metadata.full_name). - Migración 0005: policy RLS profiles_insert_self con check (id=auth.uid() and rol='alumno') — impide escalación a admin desde el self-heal. - Backfill manual ejecutado en prod para los 8 users @uabc.edu.mx que ya existían en auth.users pero no tenían profile. --- src/middleware.ts | 22 +++++++++++++++++-- .../migrations/0005_profiles_insert_self.sql | 18 +++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 supabase/migrations/0005_profiles_insert_self.sql diff --git a/src/middleware.ts b/src/middleware.ts index d4eff8a..1a8c0c6 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -19,11 +19,29 @@ export const onRequest = defineMiddleware(async (context, next) => { context.locals.profile = null; if (user) { - const { data: profile } = await supabase + const select = 'id, email, nombre, matricula, rol, semestre, tutor_id, foto_path'; + let { data: profile } = await supabase .from('profiles') - .select('id, email, nombre, matricula, rol, semestre, tutor_id, foto_path') + .select(select) .eq('id', user.id) .maybeSingle(); + + // Self-heal: el trigger prestamos_on_auth_user_created coexiste con otro + // trigger del proyecto vecino en auth.users y no siempre dispara. Si el + // user existe pero no hay profile, lo creamos aquí con los datos del OAuth. + if (!profile && user.email?.toLowerCase().endsWith(UABC_DOMAIN)) { + const nombre = + (user.user_metadata?.full_name as string | undefined) ?? + (user.user_metadata?.name as string | undefined) ?? + null; + const { data: creado } = await supabase + .from('profiles') + .upsert({ id: user.id, email: user.email, nombre }, { onConflict: 'id' }) + .select(select) + .maybeSingle(); + profile = creado ?? null; + } + context.locals.profile = profile ?? null; } diff --git a/supabase/migrations/0005_profiles_insert_self.sql b/supabase/migrations/0005_profiles_insert_self.sql new file mode 100644 index 0000000..0cc197b --- /dev/null +++ b/supabase/migrations/0005_profiles_insert_self.sql @@ -0,0 +1,18 @@ +-- 0005_profiles_insert_self.sql +-- Permite al usuario autenticado insertar su propio profile. +-- Necesario para el self-heal del middleware cuando el trigger +-- prestamos_on_auth_user_created no dispara (coexiste con otro trigger +-- del proyecto vecino en auth.users que a veces impide la ejecucion). +-- rol='alumno' obligatorio en el CHECK -> el user no puede promoverse +-- a admin insertando; solo el admin puede modificar rol via +-- profiles_admin_all (definida en 0001). + +begin; + +drop policy if exists profiles_insert_self on prestamos.profiles; + +create policy profiles_insert_self on prestamos.profiles + for insert to authenticated + with check (id = auth.uid() and rol = 'alumno'); + +commit;