Auth Flow Design en Supabase: El Patrón de Validación Jerárquica que Previene el 90% de Brechas de Seguridad

Auth Flow Design en Supabase: El Patrón de Validación Jerárquica que Previene el 90% de Brechas de Seguridad

Programación· 6 min de lectura

El 90% de los Desarrolladores Implementa Auth en Supabase Como Si El Navegador Fuera Confiable

Vuestra auth de Supabase tiene un problema que nadie menciona.

El JWT que Supabase sirve al cliente es un token legible. Cualquiera puede abrir devtools, copiar el payload, y modificarlo. No es теория — es la realidad del protocolo.

El problema real no es que Supabase sea inseguro. Es que tratáis RLS como vuestra única línea de defensa mientras ignoráis que el cliente es un entorno hostil.

La mayoría implementa auth así: configuran OAuth o magic links, añaden una policy RLS, y se olvidan. Confían ciegamente en que Row Level Security protege todo.

No lo hace.

Por Qué RLS No Es Suficiente Para Proteger Vuestra API

Supabase Auth sirve JWTs client-side mediante gotrue-js. Esto significa que el token llega al navegador en texto plano — base64-encoded, no cifrado.

Cualquiera puede decodificarlo.

Abrid devtools → Application → Cookies o localStorage → cogéis el access_token → lo pegáis en jwt.io. Veréis el payload completo: user_id, role, app_metadata con vuestras custom claims.

Ahora imaginad esto: un usuario logueado inspecta su token, cambia el valor de subscription_tier de "free" a "pro", reenvía la petición. RLS evalúa contra la base de datos, cierto. Pero si vuestra policy проверяет el rol del JWT directamente...

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

El usuario acaba de escalar privilegios desde el navegador. La policy se ejecuta server-side, sí. Pero el valor viene del JWT — que el cliente controla.

Evitad: Policies que leen directamente del JWT sin validación server-side.

Evitad: Confiar en que el token no ha sido manipulado.

Cómo Supabase Maneja JWTs: La Realidad Que No Queréis Escuchar

El flujo estándar de Supabase Auth funciona así:

  1. Usuario se autentica (OAuth, magic link, password)
  2. Supabase genera un JWT con claims básicos: sub (user_id), role, exp, iat
  3. gotrue-js almacena el token en localStorage o sessionStorage
  4. Cada request incluye el token como Authorization header
  5. Supabase valida firma, expira, y aplica RLS policies

El token contiene app_metadata donde se inyectan custom claims. Estos se actualizan cuando refreshéis el token o cuando modificáis los metadatos del usuario desde el dashboard o via API.

El payload es legible. La firma es verificable. La expiración es enforceable. Pero la lógica de authorization no puede depender ciegamente de lo que el cliente envía.

El Flujo OAuth en Supabase

OAuth en Supabase utiliza PKCE (Proof Key for Code Exchange) para authorization code flows. Esto significa:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

PKCE mitiga ataques de interceptación del authorization code. Pero no protege contra manipulated tokens post-authentication.

El Flujo de Magic Links

Magic links omiten passwords: se envía un email con un enlace que contiene un token. El flujo:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

El problema no es el flujo. Es que una vez obtenido el JWT, no hay forma de distinguir si vino de magic link, OAuth, o password — todas producen tokens con la misma estructura.

El Framework de Validación Jerárquica: 3 Capas Para Auth Seguro

He diseñado un framework que transforma cómo implementáis auth en Supabase. Lo llamo el Patrón de Validación Jerárquica en 3 Capas.

Capa 1: Validación de Firmas y Claims Estructurales

Nunca confiéis en un JWT sin verificar su firma criptográficamente. Supabase lo hace por defecto, pero debéis verificar en Edge Functions:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Capa 2: Custom Claims Via Database, No Via JWT

El secreto que nadie explica: las custom claims en el JWT son útiles para metadata, no para authorization decisions.

La arquitectura correcta:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Capa 3: Re-validation Periódica en Edge Functions

No basta con verificar una vez. Las authorizations cambian: un usuario puede downgrade, perder acceso organization, o ser baneado. Si vuestro token tiene expires_in de 3600 segundos, estáis confiando en datos stale durante una hora entera.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Implementación Práctica: Auth Flow Completo con Custom Claims

Vamos a implementar un flujo completo que use OAuth con GitHub, magic links como fallback, y custom claims correctamente validados.

Paso 1: Configurar Supabase Auth con Providers

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Paso 2: Función de Hook Post-Authentication

Supabase permite webhooks o database functions que ejecutan después de auth events:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Paso 3: Middleware de Verificación en Edge Functions

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Paso 4: Proteger Rutas con Policies Robustas

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

OAuth vs Magic Links: Cuándo Usar Cada Uno

OAuth (GitHub, Google, etc.)

✅ Usad OAuth cuando:

  • Queréis reducir fricción de registro
  • Necesitáis datos de perfil del provider (avatar, email verificado)
  • Queréis auth social para comunidad

❌ No usad OAuth cuando:

  • Trabajáis con usuarios que no tienen cuenta en providers populares
  • Operáis en mercados donde ciertos providers están bloqueados
  • Necesitáis control total sobre el flujo de verificación

Magic Links

✅ Usad magic links cuando:

  • Queréis zero password UX
  • Operáis en sectores regulados donde OAuth puede ser overkill
  • Necesitáis email verification implícita

❌ No usad magic links cuando:

  • Usuarios esperan inmediatez total (magic links pueden tardar en llegar)
  • Operáis en regiones donde emails transaccionales van a spam frecuentemente

Supabase vs Firebase: La Comparación de Auth Que Nadie Hace Bien

En el contexto de 2026, ambos ofrecen auth funcional. Pero las diferencias arquitectónicas importan:

Supabase Auth:

  • JWTs client-side con gotrue-js
  • RLS policies server-side
  • Custom claims via app_metadata
  • Full SQL access para authorization logic

Firebase Auth:

  • ID tokens client-side
  • Security rules server-side (no SQL)
  • Custom claims via token
  • Menos flexibilidad para authorization complex logic

La diferencia crucial: Supabase permite authorization decisions basadas en queries SQL contra datos relacionales. Firebase Security Rules es más limitado en exprimir lógica complexa.

Si vuestra auth requiere authorization granular basada en relaciones entre entidades, Supabase gana por arquitectura.

Si sóis un MVP rápido sin relaciones complexas entre usuarios, Firebase es más rápido de setup.

Resumen de Key Takeaways

  1. Nunca confiéis en el JWT como source of truth para authorization decisions. El cliente controla el token.
  2. User authorization debe vivir en tablas de la base de datos, no en custom claims del JWT. Las claims son útiles para display, no para security.
  3. Implementad el Patrón de Validación Jerárquica en 3 Capas: verificación de firma → custom claims vía database → re-validación periódica.
  4. OAuth y magic links son ambos seguros si se implementan con PKCE y validación server-side. La diferencia está en UX y provider availability.
  5. RLS es una herramienta poderosa, pero no es vuestra única línea de defensa. Necesitáis architecture que trate el cliente como entorno hostil.
  6. En Supabase vs Firebase, Supabase gana en flexibilidad de authorization porque permite SQL queries para decisiones complexas sobre relaciones entre entidades.

El auth flow que diseñáis hoy determina vuestra surface de ataque mañana. Tratad cada JWT como potencialmente manipulado, y construiréis sistemas que resisten la realidad del desarrollo en producción.

Artículos relacionados

---

¿Quieres recibir contenido como este cada semana? Suscríbete a mi newsletter

Brian Mena

Brian Mena

Ingeniero informatico construyendo productos digitales rentables: SaaS, directorios y agentes de IA. Todo desde cero, todo en produccion.

LinkedIn