Consolidar dos sistemas de autenticación sin dejar fuera a los usuarios existentes
Aprende a consolidar dos sistemas de autenticación de forma segura: elige el correcto, migra usuarios, elimina cookies y tablas sobrantes y haz el despliegue sin cerrar sesión a usuarios.

El problema: dos inicios de sesión en una app y usuarios atrapados en el medio
Dos sistemas de inicio de sesión separados crean un estado intermedio extraño para los usuarios. Inician sesión en una pantalla y luego son reenviados a otro prompt de login en otro lugar. Puede parecer correcto durante pruebas locales, mientras que los usuarios reales reportan cierres de sesión aleatorios, acceso perdido o bucles de redirección sin fin.
Las pistas suelen ser poco glamorosas pero obvias una vez que las miras: dos pantallas de login diferentes, dos conjuntos de cookies con nombres parecidos y comprobaciones de auth mezcladas que a veces buscan una cookie de sesión y otras esperan un JWT. En la pestaña de red puedes ver un endpoint que establece una cookie como session mientras otro pone algo como auth_token.
Esto aparece mucho en prototipos rápidos y código generado por IA. Una herramienta añade un paquete de auth listo para usar, luego un prompt posterior añade un login personalizado "solo por ahora", o una plantilla de framework se fusiona con una API existente. Nada reemplaza completamente la ruta antigua, así que ambas permanecen vivas.
Lo que suele romperse primero es la fontanería alrededor del login, no el formulario en sí:
- Los restablecimientos de contraseña actualizan una tabla de usuarios, pero la app lee la otra.
- La verificación de correo se aplica en un flujo pero se omite en el otro.
- Roles y permisos comprueban diferentes claims o distintas columnas en la BD.
- Las sesiones de "Recordarme" nunca coinciden con el middleware que protege las páginas.
La parte peligrosa es que eliminar la pieza equivocada puede dejar fuera a usuarios reales sin aviso. Borras una cookie "duplicada" y crees que limpiaste, pero esa cookie quizá era lo único que mantenía sesiones antiguas válidas. O eliminas una tabla de sesiones "sin usar" y descubres que jobs en background (o un cliente móvil) dependían de ella para refrescar tokens.
Trata la consolidación como una migración de usuarios, no como una limpieza de código. El objetivo es una fuente de verdad clara, con un solapamiento controlado para que las cuentas y sesiones existentes sigan funcionando.
Haz un inventario claro de lo que existe ahora mismo
Antes de cambiar nada, lista cada pieza móvil de auth. La mayoría de los fallos en consolidación ocurren porque alguna comprobación invisible (middleware, un guard de API, una función edge) todavía espera la cookie o el formato de sesión antiguo.
Nombra los dos enfoques en términos claros, incluso si el código es un desastre. Por ejemplo: "NextAuth cookie session" vs "JWT personalizado en localStorage". Luego anota qué controla cada sistema: la UI de login, el acceso a la API, los registros en la BD, las páginas de administración, los jobs en background y cualquier callback de terceros.
Para cada sistema, responde estas preguntas:
- ¿Qué prueba que el usuario está autenticado (nombre de cookie, header, formato del token)?
- ¿Dónde se aplica (middleware, rutas del servidor, funciones edge, handlers de API)?
- ¿Qué datos lee y escribe (tabla users, tabla sessions, refresh tokens, roles)?
- ¿Qué páginas o endpoints dependen de ello (admin, facturación, uploads, webhooks)?
- ¿Qué secretos y reglas de validación están implicadas (JWT secret, claves OAuth, hashing de contraseñas)?
Mientras inventarías, marca riesgos de producción que ya puedas ver: secretos comprometidos en el repo, chequeos de tokens que decodifican pero no verifican, SQL construido desde strings y rutas que omiten comprobaciones de permisos.
Cómo elegir el sistema de auth que conservar
El objetivo no es elegir el sistema "mejor" en teoría. Es elegir el que sea más seguro de operar, más fácil de mantener y menos probable de sorprenderte a las 2 a. m.
Empieza con tus prioridades. Para la mayoría de equipos, seguridad y mantenibilidad pesan más que conveniencia. Si nadie del equipo entiende uno de los sistemas, eso es un riesgo real aunque parezca pulido.
Después, confirma cuál de los sistemas usan realmente los usuarios. No confíes en la UI. Revisa logs de inicio de sesión, escrituras recientes en tablas de sesiones y tickets de soporte que mencionen "no puedo iniciar sesión" o "me desconectaron otra vez". A menudo un sistema maneja la mayor parte del tráfico y el otro solo se usa en flujos marginales.
Finalmente, confirma las funcionalidades necesarias. El sistema que conserves debe soportar lo que tu app necesita hoy.
Lista rápida para decidir
Conserva el sistema que cubra tus imprescindibles con menos código personalizado:
- Métodos de login que necesitas (email/contraseña, magic links, social login, SSO)
- Roles, equipos y permisos (si tu app los tiene)
- Valores predeterminados seguros (manejo de contraseñas, rotación de tokens, protección CSRF donde haga falta)
- Límites claros en el código (un lugar para crear sesiones, un lugar para comprobar auth)
- Depuración sencilla y mensajes de error claros
Si estás atascado, un desempate práctico es por límites y modelo de datos. La opción que esparce comprobaciones de auth por rutas aleatorias, escribe en varias tablas "por si acaso" y crea cookies extra seguirá causando errores.
Ejemplo: si la auth del framework maneja de forma fiable cookies de sesión, mientras que un login personalizado también establece su propia cookie y fila de sesión, conserva la auth del framework y migra el flujo personalizado dentro de ella.
Entiende a tus usuarios, sesiones y el modelo de datos antes de tocar código
Antes de consolidar nada, ten claro qué significa "un usuario" en tu app ahora. Dos sistemas de auth suelen crear dos identidades para la misma persona, y la app cambia entre ellas en silencio. Omitir este paso conlleva cierres de sesión aleatorios, pérdida de datos y confusión de cuentas.
Traza cómo se crea y empareja un registro de usuario. ¿Los inicios de sesión están ligados al email, a un UUID interno, a un provider ID (Google/GitHub) o a una mezcla? Fíjate en casos límite: cambios de email, diferencias de mayúsculas (User@ vs user@) y usuarios que se registraron dos veces con métodos distintos.
Verifica dónde viven realmente las contraseñas. Un sistema puede almacenar hashes en una tabla users, mientras que el otro nunca guardó contraseñas (por ejemplo, solo social login). No asumas que puedes "mover contraseñas" entre sistemas. En la mayoría de los casos no puedes y no debes.
Luego mapea sesiones de extremo a extremo: qué se establece en el navegador, qué se guarda en el servidor y qué comprueba la app en cada petición. Es común encontrar varios tipos de sesión activos a la vez.
Escribe una foto rápida en una página:
- Identificadores de usuario: columnas y reglas usadas para emparejar cuentas
- Métodos de auth habilitados: contraseña, magic link, proveedores OAuth
- Mecanismo de sesión: cookies, sesiones respaldadas en BD, JWTs, refresh tokens
- Dónde se almacena el estado de sesión: tablas, caché, localStorage
- Tablas duplicadas: dos tablas users, dos tablas sessions, tablas de perfil “shadow”
También busca duplicados "inocentes" que no lo son, como una tabla profiles que sí es la fuente real de permisos o estado de suscripción.
Diseña la migración para que las cuentas existentes sigan funcionando
El estado objetivo es una fuente de verdad para identidad (quién es el usuario) y autorización (qué puede hacer). Todo lo demás se convierte en una capa de compatibilidad que podrás eliminar después.
Decide cuál será el registro de usuario real. Ese registro debe poseer el ID canónico del usuario, el email y los campos de rol/permiso. Luego haz que cada ruta de login acabe en ese mismo ID de usuario, incluso si la petición empieza en el sistema heredado.
Si los sistemas usan IDs de usuario distintos, crea un mapeo. La forma más segura es un puente explícito: guarda el ID antiguo en el registro del usuario que conservas, o añade una pequeña tabla de mapeo. Evita intentar reescribir IDs en el sitio si se referencian en muchas tablas.
Las contraseñas son donde las migraciones más suelen romper a los usuarios. Si puedes reutilizar hashes de contraseña de forma segura, copia el hash y la metadata que necesita (algoritmo, salt, costes) y mantén el verificador antiguo por un tiempo. Obliga a resets solo cuando no puedas validar los hashes antiguos o cuando un problema de seguridad lo exija.
Establece políticas para casos límite comunes antes de empezar:
- Emails duplicados: elige un ganador por último login o email verificado y aísla el otro.
- Perfiles faltantes: crea perfiles mínimos en el primer login exitoso.
- Usuarios de prueba y datos seed: márcalos y exclúyelos del recuento de migración.
- Sesiones huérfanas: expíralas de forma segura en lugar de intentar "repararlas".
Ejemplo: si tienes auth del framework más un login personalizado, conserva la tabla de usuarios del framework e importa usuarios personalizados con un campo old_id. Los usuarios siguen iniciando sesión y retiras la ruta personalizada gradualmente.
Plan paso a paso para desplegar sin cierres de sesión masivos
La ruta más segura es un solapamiento corto donde la app puede leer ambos sistemas, luego un paro controlado de escrituras en el antiguo. Quieres que las sesiones existentes sigan funcionando mientras los nuevos inicios de sesión pasan a la nueva fuente de verdad.
Empieza en los bordes: middleware que lee cookies, guards de auth de la API y código de búsqueda de sesiones. Ahí es donde suelen aparecer cierres de sesión sorpresa.
Un despliegue práctico:
- Fase 1 (compat): acepta ambos formatos de token/cookie y mapea ambos al mismo ID interno de usuario.
- Fase 2 (migración silenciosa): cuando llegue una petición con una sesión antigua, vuelve a emitir una sesión nueva y establece la cookie nueva junto a la antigua.
- Fase 3 (cambio de escritura): crea nuevas sesiones solo en el sistema conservado. Deja de escribir en la tabla de sesiones antigua y deja de establecer la cookie vieja en el login.
- Fase 4 (limpieza): tras una fecha de corte clara y monitorización, elimina rutas de código antiguas, cookies y tablas.
Entre fases, observa señales reales antes de avanzar: tasa de éxito de login, picos de 401/403, errores de "usuario no encontrado" y tickets de soporte sobre cierres de sesión. Si algo salta, revierte el cambio de escritura (Fase 3) antes de eliminar la compatibilidad de lectura (Fase 1).
Ejemplo: si la app establece tanto una cookie del framework como una JWT personalizada, sigue leyendo ambas durante una o dos semanas, pero solo emite la cookie del framework después del cambio.
Cookies y tokens: elimina extras sin romper sesiones
Las cookies y los tokens son donde la consolidación falla primero. Un sistema puede usar una cookie de sesión firmada, mientras el otro usa un JWT más un refresh token. Los usuarios acaban autenticados en un sitio y desconectados en otro.
Comienza listando cada cookie y token relacionado con auth que tu app establece, y dónde se establece. Incluye middleware del servidor, código cliente y helpers del framework. Esta es la única forma segura de eliminar cookies de auth duplicadas.
Inventaria lo esencial:
- Nombre de la cookie y propósito (session, refresh, CSRF, "remember me")
- Quién la pone (ruta del servidor, código cliente, plugin del framework)
- Ámbito (dominio, path, SameSite, Secure, HttpOnly)
- Cómo se valida (clave de firma, clave de cifrado, secreto del token)
- Dónde se lee (API, páginas SSR, middleware edge)
Las colisiones de cookies son un problema oculto común. Dos sistemas pueden reutilizar el mismo nombre de cookie con claves de firma distintas, o fijar cookies en ámbitos distintos (app.example.com vs example.com). Eso puede causar cierres de sesión aleatorios, bucles infinitos de redirección o usuarios autenticados con la sesión equivocada.
Si encuentras una colisión, planifica renombrar en lugar de intentar que ambos funcionen para siempre. Introduce un nuevo nombre de cookie para el sistema que vas a conservar, acepta ambos brevemente y luego elimina la antigua.
El logout necesita cuidado extra durante el solapamiento. Los usuarios harán clic en "cerrar sesión" una vez y esperan que todo se limpie. Durante la migración, haz que el logout borre ambas cookies (y revoque tokens en el servidor si usas refresh tokens). Si no, puedes tener un inicio de sesión fantasma donde la cookie antigua les vuelve a autenticar inmediatamente.
Ejemplo: una herramienta IA añadió sesiones de NextAuth y un JWT personalizado llamado auth. Si coexisten, tu servidor puede aceptar NextAuth mientras el cliente sigue enviando el JWT. Elige uno, renombra la cookie si hace falta y añade un puente temporal que convierta una cookie antigua válida en la nueva sesión.
Limpieza de base de datos: sesiones, usuarios y tablas heredadas
Los errores en la base de datos son como la consolidación se convierte en cierres de sesión masivos o fallos de seguridad. Trata la limpieza como una migración, no como un botón de borrar.
Mapea cada tabla relacionada con auth y decide qué ocurre con ella:
- Conservar: usada activamente por el sistema elegido (users, sessions, refresh tokens)
- Fusionar: contiene datos reales de usuario que debes traer adelante (profiles, emails, hashes de contraseña)
- Archivar: útil para soporte y rollback (sesiones heredadas, cuentas antiguas)
- Eliminar: realmente sin uso después de validar
Antes de tocar producción, escribe migraciones reversibles. En lugar de borrar filas de sesiones heredadas, cópialas a una tabla de archivo con un timestamp y después cambia la app para que deje de leer la tabla heredada.
Haz que las referencias sean aburridas (y correctas)
Los datos de auth no están aislados. Roles, membresías de organización, permisos y logs de auditoría suelen apuntar a un ID de usuario específico. Si la nueva auth usa IDs distintos, necesitas un plan claro de traducción.
Un enfoque simple es añadir un campo de mapeo estable (como legacy_user_id) en la tabla de usuario que conservas, migrar usuarios y luego actualizar referencias en pequeños lotes. Haz lo mismo con sesiones: si tenías sessions y user_sessions, elige una fuente de verdad y adapta el código.
Una secuencia de despliegue que reduce sorpresas:
- Backfill de nuevas tablas y columnas de mapeo sin cambiar comportamiento.
- Actualiza la app para leer de la nueva fuente mientras temporalmente escribe en ambas.
- Verifica que roles/orgs/permisos coinciden para cuentas reales (incluidos admins).
- Cambia escrituras a las tablas conservadas solamente.
- Archiva y luego elimina las tablas heredadas tras un periodo de calma.
También confirma que nada sigue tocando las tablas antiguas: jobs en background, dashboards de admin, scripts de analytics, herramientas de soporte y cron tasks. Ahí es donde suele esconderse un segundo camino de auth.
Errores comunes que desconectan usuarios o abren agujeros de seguridad
La forma más rápida de crear una avalancha de tickets es eliminar piezas antiguas antes de que la nueva ruta maneje todos los flujos reales de usuarios. La app puede parecer bien en tu navegador, pero los usuarios en producción tienen cookies antiguas, sesiones a medio expirar y enlaces guardados.
Errores que causan cierres de sesión masivos o nuevos fallos:
- Deshabilitar el middleware de auth antiguo demasiado pronto, de modo que las sesiones existentes dejan de reconocerse.
- Cambiar rutas de restablecimiento o verificación a mitad de migración, lo que rompe emails ya enviados o verifica la cuenta equivocada.
- Dejar las comprobaciones de tokens demasiado permisivas (aceptar tokens sin verificación adecuada, omitir checks de issuer/audience o no expirar sesiones).
- Olvidar otros clientes (apps móviles, herramientas de admin, jobs en background, webhooks entrantes) que aún envían la cookie o token antiguo.
- No probar el comportamiento de cookies entre subdominios y entornos (localhost vs staging vs producción), provocando que cookies se pierdan por flags de dominio, SameSite o Secure.
Un ejemplo concreto: borras la cookie de sesión antigua porque el nuevo sistema usa JWTs, pero una herramienta admin embebida en admin.yoursite.com solo conoce la cookie de sesión. Rompe, y alguien "arregla" deshabilitando comprobaciones de auth en esa ruta. Así es como las migraciones crean agujeros de seguridad.
Dos medidas de seguridad reducen el riesgo:
- Mantén ambos validadores activos brevemente y registra cuál se usó por petición.
- Congela rutas URL para resets/verificaciones hasta que los emails antiguos hayan expirado y los redireccionamientos estén confirmados.
Comprobaciones rápidas para ejecutar antes y después del despliegue
La limpieza más segura es la que puedes deshacer rápido. Los cambios de código son solo la mitad del riesgo. La otra mitad es lo que ocurre en navegadores reales con cookies antiguas y sesiones a medio expirar.
Antes del lanzamiento, confirma:
- El inventario está completo: cada nombre de cookie, header token, store de sesión y tabla relacionada con auth está documentada.
- Un sistema es la fuente de verdad para identidad y roles, y cada ruta lee de él.
- Los nombres y ámbitos de cookies están confirmados (dominio, path, SameSite, Secure), con un plan para ignorar o reemplazar cookies antiguas.
- Existe un plan de rollback que esté a un deploy de distancia (p. ej., un feature flag que reactive la comprobación de sesiones antigua).
- Tu matriz de pruebas está escrita y ejecutada para signup, login, logout, reset de contraseña y comprobaciones de roles/permiso.
No confíes en una cuenta que pasa por el camino feliz. Prueba al menos un usuario creado bajo cada sistema antiguo y uno "complicado" que tenga ambas cookies. Ahí se esconden los bugs.
Después del lanzamiento, vigila el comportamiento más que el código:
- Monitoriza fallos de login por motivo (token inválido, sesión ausente, CSRF mismatch, rol denegado), no solo el total.
- Rastrea la tasa de creación de sesiones y compárala con el tráfico normal.
- Confirma que la tabla de sesiones antigua y tablas de auth heredadas no reciben escrituras nuevas tras el corte.
- Prueba flujos en una ventana de incógnito y en un navegador sucio que aún tenga cookies antiguas.
- Cuando esté estable, elimina secretos y claves antiguas, borra sesiones viejas y solo entonces elimina tablas sobrantes.
Un ejemplo realista: fusionar una auth del framework con un login personalizado
Una situación común generada por IA se ve así: la UI usa NextAuth (cookies, callbacks, una tabla sessions), luego alguien añade un login JWT personalizado "solo para la API". Ahora tienes dos fuentes de verdad sobre quién es un usuario.
Los síntomas confunden. Un usuario puede iniciar sesión y navegar la UI, pero llamadas a la API devuelven 401 porque esperan un Bearer token. O la API funciona en Postman con un JWT, pero la UI sigue rebotando al login porque falta la cookie de sesión. Peor aún, el mismo email puede acabar con dos IDs de usuario distintos según qué ruta creó la cuenta.
La solución más segura es elegir un ganador y migrar sin forzar a todos a registrarse de nuevo. Si consolidas en NextAuth, mantén la ruta JWT personalizada brevemente como capa de compatibilidad.
Una migración práctica:
- Elige el ID de usuario real (a menudo la tabla users de NextAuth) y mapea usuarios que solo tenían JWT a ese ID.
- Acepta temporalmente ambos: permite que la API se autentique vía cookie de sesión o el JWT antiguo, pero resuelve ambos al mismo ID real en el código del servidor.
- Cuando los usuarios vuelvan a iniciar sesión (o refresquen), emite solo el método de sesión conservado y deja de crear nuevos JWTs.
- Tras una ventana corta, elimina la verificación de JWT, borra la cookie extra y retira las tablas/tokenes no usadas.
Una limpieza segura termina con una cookie, un store de sesiones y un ID de usuario usados en todas partes.
Siguientes pasos: cuándo pedir ayuda
Si no puedes explicar en una frase cómo una petición se convierte en un usuario autenticado en tu app, para. Estos cambios parecen pequeños, pero pueden bloquear usuarios en silencio o debilitar la seguridad.
Señales para parar y pedir una segunda opinión
Has pasado de "limpieza simple" a "proyecto de migración" si:
- No puedes mapear cada usuario a una identidad (dos tablas de usuarios, emails desajustados, IDs duplicados).
- No sabes cómo se hashearon las contraseñas (o ves más de un método de hashing).
- Las comprobaciones de rol están mezcladas (algunas rutas usan middleware, otras una cookie personalizada, otras consultan la BD).
- Sesiones y cookies se solapan (aparentemente están logueados, pero llamadas a la API fallan o la identidad cambia).
Qué preparar para una revisión
Obtendrás mejores respuestas si reúnes un pequeño paquete de hechos primero:
- Acceso al repo (o una exportación limpia) más la configuración de despliegue actual
- Lista de env vars relacionadas con auth (nombres de cookies, secretos JWT, IDs de cliente OAuth, URLs de callback)
- Un volcado del esquema de BD y recuentos de filas para users, sessions y otras tablas de auth
- Logs recientes sobre login, refresh de tokens y errores no autorizados
- Una nota corta sobre lo que reportan los usuarios (logouts forzados, cuentas equivocadas, acceso admin roto)
Si este lío fue creado por herramientas de IA y necesitas un plan de limpieza orientado a producción, FixMyMess (fixmymess.ai) ayuda a los equipos a diagnosticar autenticaciones enmarañadas, eliminar cookies duplicadas y rutas de sesión y endurecer el código antes de que algo se rompa en producción.
Preguntas Frecuentes
How do I confirm I actually have two auth systems?
Empieza por listar cada punto de entrada de inicio de sesión, cookie, token, tabla de sesiones y guardia de auth. En muchas apps rotas, el problema real no es el formulario de login sino el middleware oculto y las comprobaciones de la API que todavía esperan el formato heredado.
Which auth system should I keep?
Mantén el que ya atiende a la mayor parte del tráfico real de usuarios y que tenga los valores predeterminados más claros y seguros. Si un sistema es “pegamento personalizado” repartido en rutas aleatorias y el otro tiene un flujo de sesiones bien definido, el centralizado suele ser la opción más segura.
Why can’t I just delete the extra login and cookie?
Porque eliminar un inicio de sesión o cookie “duplicada” suele romper sesiones existentes y bloquear usuarios en silencio. Trátalo como una migración de usuarios: mantén un solapamiento corto donde puedas leer ambos formatos, luego cambia las escrituras y finalmente limpia.
How do I avoid creating two identities for the same person?
Elige un registro de usuario canónico y haz que cada ruta de autenticación resuelva ese mismo ID interno. Si los dos sistemas usan IDs diferentes, añade un mapeo explícito (por ejemplo, un campo old_id o una tabla de mapeo) en lugar de reescribir IDs por todas partes de golpe.
Can I migrate passwords from the old system to the new one?
Generalmente no, y no deberías intentarlo a menos que comprendas completamente el formato antiguo de los hashes y su metadata (algoritmo, salt, factors). Una opción más segura es seguir verificando los hashes antiguos durante el periodo de transición o exigir un restablecimiento solo cuando realmente no puedas validar las credenciales antiguas.
What’s the safest way to remove extra cookies and tokens?
Haz un inventario explícito de cookies y tokens: nombres, ámbitos, quién las crea y quién las lee. Durante el despliegue, acepta ambos brevemente, luego solo emite la cookie que vas a mantener y asegúrate de que el cierre de sesión borre tanto la cookie vieja como la nueva para evitar inicios de sesión “fantasma”.
What rollout plan prevents mass logouts?
Usa fases: compatibilidad de lectura primero, luego reemisión silenciosa de nuevas sesiones cuando aparezcan las antiguas, después deja de escribir en el sistema viejo y solo más tarde elimina tablas y código obsoletos. Avanza de fase solo cuando veas tasas de inicio de sesión estables y sin picos de 401/403.
What if both systems use similar cookie names or scopes?
Renombra la cookie que mantienes a un nombre nuevo y único y acepta la antigua solo temporalmente. Las colisiones suceden porque dos sistemas reutilizan un mismo nombre de cookie con claves distintas o con ámbitos diferentes (dominio/path), lo que puede provocar bucles de redirección y cierres de sesión aleatorios.
How do I clean up duplicate users/sessions tables safely?
No borres primero; archiva y demuestra que nadie escribe en las tablas heredadas. Una estrategia práctica es rellenar campos de mapeo, cambiar lecturas, escribir en ambos brevemente si es necesario, cambiar escrituras y luego archivar sesiones y cuentas heredadas antes de eliminar.
When should I bring in help (and what should I prepare)?
Si no puedes explicar en una frase cómo una petición se convierte en un usuario autenticado en tu app (UI, API y jobs en background), estás en territorio de migración. Si esto fue causado por código generado por IA y necesitas un plan de consolidación seguro rápido, FixMyMess puede ejecutar una auditoría de código gratuita y ayudarte a unificar auth sin romper a los usuarios existentes.