Incidencias resueltas
Memoria institucional: qué pasó, por qué, y cómo se solucionó. Orden cronológico inverso.
19/09/2026 — Cadena de fallos en la recuperación de contraseña
Reportado como un único síntoma ("no me llegan los emails de recuperación / el enlace inicia sesión directamente") que resultó ser tres causas independientes, encontradas y corregidas una por una.
1. Secreto del webhook desincronizado
Síntoma: los correos de autenticación no seguían la plantilla
personalizada ni dejaban registro en email_log.
Causa raíz: AUTH_EMAIL_HOOK_SECRET en Vercel y
el secreto que Supabase Auth usaba para firmar las llamadas al Send
Email Hook llevaban tiempo desincronizados — nunca se había
ejecutado supabase config push desde que se activó el
hook. Cada llamada fallaba la verificación (401), Supabase caía a su
plantilla nativa por defecto.
Solución: secreto regenerado y fijado a la vez en Vercel
(vercel env add) y en Supabase (supabase config push), evitando depender de adivinar cuál de los dos valores existentes
era el correcto.
supabase/config.toml (Auth, no solo migraciones SQL)
necesita config push explícito. No pasa solo.
2. Zona horaria incorrecta en los logs
Síntoma: las fechas en los logs de email, auditoría, PDFs e historial de socios aparecían con 1-2 horas menos de la hora real de España.
Causa raíz: Vercel ejecuta sus funciones en UTC.
toLocaleString("es-ES") sin
timeZone explícito usa la zona horaria del proceso, no
la del lector.
Solución: src/lib/format/fecha.ts, con
timeZone: "Europe/Madrid" forzado en todo formateo de
fecha del lado servidor (y también del cliente, por consistencia, ya
que la plataforma es de uso exclusivo en España).
3. Dominio de Resend cayendo en spam
Síntoma: el correo salía bien de Resend (estado
Delivered) pero Gmail lo clasificaba como spam.
Causa raíz: faltaba un registro DMARC para el dominio
(tecvalencia.com). Al añadirlo, se creó un segundo
registro _dmarc sin borrar ningún anterior —
dos registros DMARC en el mismo host invalidan la evaluación
completa
para cualquier proveedor de correo.
Solución: un único registro
v=DMARC1; p=none; adkim=r; aspf=r. Confirmado: el
siguiente envío llegó a la bandeja principal.
4. Doble canje del código PKCE en /auth/callback
Síntoma: incluso con el email llegando bien y con la plantilla correcta, al pulsar el enlace de recuperación la aplicación iniciaba sesión directamente, sin pasar nunca por la pantalla de nueva contraseña.
Causa raíz: detectSessionInUrl (activado por
defecto en createBrowserClient) hace que el SDK de
Supabase detecte y canjee él solo el ?code= de la URL
en cuanto se crea el cliente — antes de que
pagina-callback.tsx llegara a su propia llamada
explícita a exchangeCodeForSession(). Esa segunda
llamada manual fallaba (código ya gastado), caía al "enlace
inválido" y navegaba a /login — pero la sesión ya se
había abierto en el primer canje automático, así que el middleware,
al ver a alguien ya autenticado en /login, lo redirigía
derecho a su sociedad.
Solución: detectSessionInUrl: false en
src/lib/supabase/client.ts. El canje queda
exclusivamente en manos de pagina-callback.tsx, la
única página que necesita interpretar esa URL.