8 min de lectura

Problemas de autenticación JWT en prototipos: expiración, rotación de refresh, desfase del reloj

Los problemas de autenticación JWT en prototipos suelen parecer aleatorios. Aprende soluciones para expiración, rotación de refresh, desfase de reloj y patrones seguros de almacenamiento de tokens.

Problemas de autenticación JWT en prototipos: expiración, rotación de refresh, desfase del reloj

Por qué los tokens JWT dejan de funcionar de forma aleatoria en prototipos

La mayoría de problemas de autenticación con JWT en prototipos aparece de la misma manera: un inicio de sesión funciona y luego los usuarios de repente reciben errores 401, son devueltos a la pantalla de inicio de sesión, o ven una app que solo funciona tras un refresco.

Parece aleatorio porque los JWT dependen del tiempo, tu prototipo suele estar compuesto por varias piezas móviles, y pequeñas diferencias se acumulan. El reloj de un portátil puede estar unos minutos desincronizado. Un servidor está en otra zona horaria o tiene drift. Una segunda pestaña del navegador conserva un token antiguo y sobrescribe el nuevo. De pronto la misma petición funciona para ti y falla para otra persona.

Un modelo mental rápido ayuda: casi todo fallo de auth cae en uno de tres grupos.

  • Tiempo: expiración (exp), emitido en (iat), desfase de reloj, tokens de acceso de corta vida
  • Almacenamiento: token faltante, sobrescrito, borrado o atrapado en el lugar equivocado
  • Validación: secreto/clave pública incorrecta, aud/iss equivocados, token revocado o rotado

Antes de cambiar código, captura lo básico. Te ahorrará horas de adivinanzas.

  • La respuesta exacta que falla (estado + mensaje) y en qué endpoint ocurrió
  • Una marca temporal del cliente y del servidor para la misma petición
  • La hora y zona horaria del dispositivo (especialmente en móvil)
  • Los claims del token (exp, iat, iss, aud) y cuándo se emitió

Ejemplo concreto: un fundador prueba en su Mac y todo funciona. Un usuario en Windows sigue desconectándose cada 10-15 minutos. El problema real no es “cierres de sesión aleatorios”: el reloj del usuario tiene 6 minutos de retraso, el access token expira rápido y el servidor lo rechaza sin tolerancia. Abre una segunda pestaña y también puedes conseguir que una pestaña refresque y la otra sobrescriba los tokens almacenados.

Si tu prototipo fue generado por herramientas como Lovable, Bolt o Cursor, es común ver un manejo inconsistente de tokens entre páginas. FixMyMess suele encontrar una mezcla de expiraciones cortas, lógica de refresh ausente y almacenamiento inseguro ocurriendo a la vez.

Fundamentos de JWT que necesitas para depurar (sin el volcado teórico)

Un JWT es solo una nota firmada. Tu servidor la firma para poder verificar después que el token fue emitido por ti y no fue alterado. La mayoría de JWT no están cifrados, así que cualquiera que tenga el token puede decodificarlo y leer su contenido.

Eso conduce a la primera regla práctica: trata los JWT como identificaciones legibles, no como bóvedas secretas. Si un token se filtra (logs, almacenamiento del navegador, capturas de pantalla, herramientas de analítica), lo que hay dentro también se filtra.

Los pocos claims que explican la mayoría de fallos

Cuando los tokens “dejan de funcionar aleatoriamente”, normalmente es uno de estos campos o cómo tu app los comprueba:

  • exp (expires at): el momento en que el token debe rechazarse.
  • iat (issued at): cuándo se creó el token. Útil para depurar problemas de tiempo.
  • nbf (not before): el token debe rechazarse hasta esa hora.
  • aud (audience): para quién está pensado el token (una API específica).
  • iss (issuer): qué sistema emitió el token.
  • sub (subject): el usuario o entidad que representa el token.

Un desajuste común en prototipos: el frontend envía un token a la API B que fue emitido para la API A (aud incorrecto), o el backend es estricto con iss en un entorno pero no en otro.

“Falla” puede significar dos cosas opuestas

Los problemas de auth con JWT a menudo provienen de validaciones ausentes o excesivamente estrictas.

Si faltan comprobaciones, los tokens podrían funcionar cuando no deberían (hueco de seguridad), y luego “de repente fallan” cuando añades una regla de validación.

Si las comprobaciones son demasiado estrictas, los usuarios son desconectados por diferencias mínimas, como un servidor 60 segundos adelantado (más sobre el desfase de reloj más adelante), o un token válido que no coincide exactamente con la cadena esperada en aud/iss.

Qué nunca debe ir en un JWT

No pongas secretos o datos sensibles en la carga útil de un JWT: contraseñas, claves de API, credenciales de base de datos, códigos de restablecimiento o datos personales que no quieras que acaben en una hoja de cálculo. Mantenlo mínimo: un ID (sub), quizá un rol o algunos permisos, y los claims temporales.

Si heredas un prototipo generado por IA, esto es un problema frecuente que vemos en FixMyMess: tokens que incluyen por accidente claves internas o demasiados datos de usuario, y luego se almacenan en lugares inseguros. Incluso si la autenticación “funciona”, está a un paso de convertirse en un incidente real.

Problemas de expiración: ajustes de exp y iat que causan cierres sorpresa

La mayoría de problemas de autenticación JWT en prototipos no son “aleatorios”. Suelen ser cálculos de expiración que parecen correctos en pruebas locales y luego se rompen cuando aparecen dispositivos reales, redes reales y diferencias temporales reales.

Unos pocos errores provocan la mayoría de cierres sorpresa:

  • exp demasiado corto. Cinco minutos suena seguro, pero es brutal para prototipos con cold starts lentos, apps móviles en segundo plano y conexiones intermitentes. Los usuarios vuelven tras una breve pausa y todas las llamadas fallan.
  • exp ausente. Algunas librerías aceptan tokens sin expiración, otras no, y tu propio código puede tratarlos como expirados. Esto crea comportamientos confusos e inconsistentes entre entornos.
  • iat en el futuro. Ocurre cuando el reloj del servidor está mal, usas la zona horaria equivocada, o generas tokens en un servicio y los validas en otro. Muchos validadores rechazan tokens “aún no válidos”.

Un patrón por defecto práctico es: token de acceso de corta duración + refresh token de larga duración. Mantén el access token lo bastante corto para limitar el daño si se roba, pero lo bastante largo para sobrevivir al uso normal. Para muchos prototipos, un buen punto de partida son 10 a 20 minutos para el acceso, y días para el refresh. Luego puedes endurecerlo.

El comportamiento del cliente importa tanto como los ajustes del token. Una buena regla es: manejar un 401 una sola vez.

  1. Si una petición devuelve 401, llama al endpoint de refresh.
  2. Si el refresh tiene éxito, reintenta la petición original una vez.
  3. Si el reintento falla o el refresh falla, cierra la sesión y muestra un mensaje claro.

Esto evita bucles de reintento infinitos y pantallas que “se quedan girando”.

En el servidor, evita errores 500 vagos cuando un token está expirado. Devuelve un 401 claro para tokens de acceso expirados o inválidos, y un 401 (o 403, si prefieres) claro cuando el refresh token es inválido o revocado. Así queda claro si tienes un problema de expiración o un fallo real en el backend.

Ejemplo: un fundador prueba en su portátil y todo funciona. Usuarios móviles abren la app después de comer, el access token expiró y la app sigue llamando a la API con el token viejo hasta que abandona. Con el patrón de refresco y reintento una sola vez, ese usuario simplemente continúa sin darse cuenta.

Si heredaste un flujo de auth generado por IA donde los tokens “a veces” fallan, FixMyMess suele encontrar en la primera auditoría una mezcla de exp demasiado corto, validación inconsistente y lógica de refresh ausente.

Desfase de reloj: cuando tokens correctos fallan porque la hora está mal

Algunos problemas de auth con JWT no son causados por código defectuoso o “tokens inválidos”. El token puede ser correcto, firmado y no modificado, y aun así fallar porque el reloj del dispositivo o del servidor está mal.

El desfase de reloj ocurre más a menudo en prototipos de lo que se espera: un teléfono con la hora manual, una VM con drift, un contenedor con una fuente de tiempo mal configurada, o dos servidores en la misma app que discrepan por un minuto.

Cuando la hora está mal, normalmente ves fallos alrededor de claims temporales:

  • nbf (not before): el servidor cree que el token aún no es válido
  • exp (expires): el servidor cree que el token ya expiró
  • iat (issued at): algunas librerías lo usan para comprobaciones extra y pueden rechazar tokens “futuros”

Un escenario común: firmas un token en el Servidor A, pero la petición del usuario se valida en el Servidor B cuyo reloj va 45 segundos adelantado. De pronto los usuarios se desconectan “aleatoriamente”, o el login funciona y luego falla al cargar otra página.

Solución práctica: añade una pequeña tolerancia al validar

La mayoría de librerías JWT permiten permitir una pequeña tolerancia al comprobar exp y nbf. Un buen punto de partida es 30 a 120 segundos. Mantenlo pequeño: la leeway sirve para manejar drift, no para alargar sesiones.

Si usas leeway, trátalo como un salvavidas, no como una tirita. Si necesitas 10 minutos de tolerancia, probablemente tienes un problema de sincronización horaria o de despliegue.

Solución operativa: haz que la hora sea consistente en todas partes

La leeway reduce fallos falsos, pero aún deberías arreglar la causa raíz. Comprobaciones rápidas que detectan la mayoría de problemas:

  • Asegura que todos los servidores y agentes de build sincronicen con la misma fuente de tiempo (NTP)
  • Evita mezclar hosts con hora correcta y contenedores con tiempo aislado o mal configurado
  • Verifica que los dispositivos móviles de prueba tengan la hora automática activada
  • En setups multi-servidor, confirma que las peticiones no reboten entre nodos con tiempos diferentes

Si estás heredando un prototipo generado por IA (común con Lovable, Bolt, v0, Cursor o Replit), los bugs de desfase de reloj pueden quedar enmascarados por el “funciona en mi máquina”. Cuando FixMyMessAudita fallos de auth, a menudo encontramos que una pequeña tolerancia más una sincronización de tiempo adecuada elimina los cierres de sesión intermitentes sin cambiar todo el diseño de autenticación.

Refresh tokens: la pieza que falta en la mayoría de flujos de prototipos

Encuentra secretos expuestos ahora
Escaneamos claves expuestas y cargas útiles JWT inseguras antes de que salgan a producción.

La mayoría de problemas de JWT en prototipos ocurren porque la app intenta usar un token para todo: un token de acceso de corta duración que además tiene que mantener al usuario conectado durante días. Esa tensión es por qué las sesiones se sienten aleatorias. Un refresh token existe para mantener la sesión viva sin hacer el access token de larga duración (lo cual es riesgoso si se filtra).

Los access tokens deberían ser aburridos: expiración corta, enviados a menudo, fáciles de reemplazar. Los refresh tokens deberían usarse raramente: únicamente para obtener un nuevo access token, y mantenerse fuera de lugares donde JavaScript o los logs puedan recogerlos fácilmente.

Un error común en prototipos es almacenar el refresh token de la misma manera que el access token (por ejemplo, en localStorage) y tratarlo como una credencial API normal. Cuando ese token se filtra, un atacante puede crear nuevos access tokens hasta que lo notes. Otro error es no tener refresh token en absoluto, así que la app “soluciona” los cierres de sesión poniendo la expiración del access token enorme. Eso suele convertirse en deuda de seguridad más adelante.

Antes de construir el flujo, decide qué debe significar “sesión” para tu producto:

  • ¿Cuánto tiempo debe permanecer un usuario conectado sin abrir la app?
  • ¿Cerrar el navegador debe desconectar al usuario o no?
  • ¿Quieres una sesión por dispositivo, o iniciar sesión en uno debe expulsar a otro?
  • ¿Qué sucede después de un cambio de contraseña: cerrar sesión en todas partes, o solo en nuevas sesiones?

El uso real añade casos límite que los prototipos raramente manejan. Los usuarios abren múltiples pestañas, las redes caen a mitad de petición y dos peticiones pueden intentar refrescar al mismo tiempo. Si no controlas eso, obtienes bucles (refresh, fallo, reintento) o 401s repentinos que parecen “problemas de auth JWT” incluso cuando tus tokens están bien.

Si heredaste un prototipo generado por IA (Lovable, Bolt, v0, Cursor, Replit), esta capa de refresh suele faltar o estar a medio implementar. Arreglarla normalmente quita primero los bugs de “funcionaba ayer” en el login.

Paso a paso: un flujo de refresh que funciona con rotación

La mayoría de problemas JWT aparecen cuando los access tokens expiran y la app no tiene una forma fiable de recuperarse. Un flujo de refresh con rotación arregla eso haciendo que la expiración sea normal, no aterradora.

El flujo (login → refresh → reintento)

Empieza emitiendo dos tokens al iniciar sesión: un access token de corta duración (minutos) y un refresh token de larga duración (días o semanas). El access token es lo que tu API comprueba en cada petición. El refresh token se usa solo para obtener un nuevo access token.

Una secuencia simple y fiable se ve así:

  • El login devuelve access token + refresh token
  • El cliente llama a las APIs con el access token hasta que falla con 401
  • El cliente llama al endpoint de refresh con el refresh token
  • El servidor devuelve un nuevo access token y un nuevo refresh token
  • El cliente reintenta la llamada API original una vez

La rotación es el detalle clave: cada refresh produce un refresh token completamente nuevo y el viejo deja de ser válido. Así, si un refresh token se filtra, deja de funcionar tan pronto como el usuario real hace un refresh.

Reglas de rotación que debes implementar del lado servidor

Para que la rotación de refresh tokens sea realmente segura (y no rompa a los usuarios aleatoriamente), mantén estas reglas estrictas:

  • Almacena los refresh tokens del lado servidor (hashados), con id de usuario, expiración y un id único de token.
  • En el refresh, acepta solo el id de token actual, márcalo inmediatamente como usado/revocado y emite un nuevo id de token.
  • Si se presenta un token antiguo de nuevo, trátalo como sospechoso y revoca toda la sesión (o exige re-login).

La concurrencia es donde los prototipos suelen fallar. Dos pestañas, un doble clic o un reintento de la app pueden disparar dos refresh al mismo tiempo. Una estrategia de gracia pequeña evita cierres sorpresa: permite que el refresh token previamente válido se use una vez más en una ventana corta (por ejemplo 10-30 segundos) si acaba de rotarse, pero solo si puedes detectar que pertenece a la misma cadena de sesión.

Si estás peleando con problemas JWT que se sienten aleatorios, normalmente es porque la rotación, el almacenamiento o la concurrencia están a medio implementar. Los equipos suelen traer a FixMyMess un prototipo de Lovable/Bolt/v0/Cursor/Replit donde el refresh parece “hecho” pero falla bajo uso real, y lo endurecemos hasta un flujo apto para producción rápido.

Patrones seguros de almacenamiento de tokens (web y móvil)

Endurece la autenticación para usuarios reales
Manejamos carreras por refresh en múltiples pestañas, reintentos y casos límite que los prototipos no consideran.

Si ves problemas de auth JWT que solo aparecen fuera de tu propio navegador, el almacenamiento suele ser la causa. Un prototipo puede sentirse bien en pruebas y luego empezar a desconectar gente cuando añades páginas reales, scripts de terceros o dispositivos distintos.

Web: trata el refresh token como una contraseña

Para apps web, la opción más segura por defecto es almacenar el refresh token en una cookie httpOnly y Secure. httpOnly evita que JavaScript la lea (muy útil si alguna vez tienes un bug XSS). Secure asegura que solo viaje por HTTPS.

Las banderas de cookie no son "configurar y olvidar". Hazlas una elección deliberada:

  • httpOnly + Secure: buena base para refresh tokens.
  • SameSite=Lax: suele funcionar para la navegación normal y reduce el riesgo CSRF.
  • SameSite=None: necesario para setups cross-site (dominios diferentes), pero requiere Secure y aumenta la exposición CSRF.
  • Protección CSRF: si el endpoint de refresh es cookie-based, añade defensas CSRF (por ejemplo, un header con token CSRF o una estrategia de doble envío).

Evita almacenar tokens de larga duración en localStorage o sessionStorage. Es cómodo, pero si cualquier script en la página puede ejecutarse (XSS, dependencia comprometida, extensión maliciosa), puede leer los tokens y enviarlos.

Los access tokens son distintos: mantenlos de corta duración y en memoria cuando sea posible. Si un refresco de página los pierde, está bien porque la cookie de refresh puede emitir uno nuevo.

Móvil: usa almacenamiento seguro, mantén acceso corto

En iOS y Android, guarda los refresh tokens en el almacenamiento seguro de la plataforma (Keychain en iOS, Keystore en Android). No pongas refresh tokens en almacenamiento plano de la app ni en logs.

Un patrón práctico:

  • Refresh token: almacenamiento seguro, larga duración, rotado.
  • Access token: sólo en memoria, expiración corta, reemplazado con frecuencia.
  • Al fondo/cerrar la app: descarta el access token y solicita uno nuevo al reanudar.

Una fuente silenciosa de fallos “aleatorios”: tokens filtrados a lugares inesperados. Nunca pongas access tokens en URLs (query params) y límpialos de logs, eventos de analítica, informes de fallos y popups de error. Por ejemplo, si tu app registra la petición completa cuando una API falla, puede capturar accidentalmente la cabecera Authorization.

Si heredaste un flujo de auth generado por IA que mezcla cookies, localStorage y access tokens de larga vida, FixMyMess puede auditarlo rápido y señalar los puntos exactos de fuga y las decisiones de almacenamiento inseguro antes de lanzar.

Trampas comunes de prototipos que hacen la auth inestable

Los bugs JWT en prototipos suelen sentirse aleatorios porque el sistema está a medias entre estricto y laxo. “Funciona en mi máquina”, luego falla tras un redeploy, añadir un segundo servicio o cuando un usuario abre otra pestaña.

Trampa 1: Omitir comprobaciones de issuer (iss) y audience (aud)

Muchos prototipos solo validan la firma y la expiración. Puede estar bien para un backend único, pero se vuelve frágil cuando añades una segunda API, un worker en background o un servicio de admin separado.

Si no validas iss y aud, puedes acabar aceptando tokens pensados para otro servicio, y luego “endurecer la seguridad” y de repente usuarios reales reciben 401s porque los tokens existentes no coinciden con las nuevas reglas.

Una forma sencilla de evitar sorpresas es decidir pronto:

  • Una cadena issuer para tu servicio de auth
  • Una audiencia por API (o una audiencia compartida si realmente tienes una sola API)
  • Configuraciones consistentes en dev, staging y prod

Trampa 2: Secretos JWT cambiando entre ambientes o redeploys

Los prototipos suelen generar secretos al arrancar, usar distintos .env por máquina o rotar claves accidentalmente al desplegar. El resultado parece “tokens que dejan de funcionar aleatoriamente”, pero el problema real es que el servidor ya no puede verificar tokens que emitió antes.

Si necesitas redeploys frecuentes, trata las claves de firma como la contraseña de la base de datos: mantenlas estables y gestionadas. Si vas a rotar claves, hazlo deliberadamente (por ejemplo, soportando la clave actual y la anterior durante un solapamiento corto).

Trampa 3: Confiar en la decodificación cliente en vez de la verificación servidor

Decodificar un JWT en el navegador (o app móvil) no es lo mismo que verificarlo. Un prototipo puede leer la carga útil, asumir que el usuario está logueado y saltarse una comprobación real al servidor hasta más tarde.

Eso crea estados confusos: la UI dice “logueado”, pero la API rechaza las peticiones. Haz que el servidor sea la fuente de la verdad y que el cliente trate el “API dice 401” como la señal real para refrescar o reautenticar.

Trampa 4: Caché incorrecto del estado de auth (especialmente entre pestañas)

Un bug común: una pestaña refresca tokens, otra pestaña sigue usando un access token antiguo y tu app cambia entre funcionar y fallar. Otra variante: cacheas un “usuario actual” en memoria y nunca lo actualizas tras un refresh.

Si tu app corre en múltiples pestañas, decide cómo se propagan las actualizaciones de estado de auth. Como mínimo, maneja eventos de “token actualizado” y “desconectado” limpiamente para no seguir enviando tokens obsoletos.

Trampa 5: Enviar atajos de auth para depuración

El código de prototipo a veces acepta tokens sin firmar, usa el algoritmo none o salta la verificación para pruebas. Si algo de eso llega a producción, obtienes tanto riesgo de seguridad como fallos extraños cuando distintas partes del sistema discrepan sobre qué es válido.

Si heredaste un prototipo generado por IA y la auth se siente inestable, FixMyMess puede auditar el código rápidamente (incluida la verificación de tokens, la lógica de rotación y el almacenamiento) y señalar exactamente cuáles de estas trampas estás pisando antes de que los usuarios lo noten.

Ejemplo: funciona para ti, pero los usuarios se desconectan

Obtén un diagnóstico experto de código
Envíanos tu base de código y recibe una lista clara de problemas antes de hacer commits.

Una historia común: pruebas la app todo el día en tu portátil y la auth parece estable. Entregas un preview a usuarios reales y de repente recibes mensajes como “me desconecta cada 10 minutos” o “el login funciona una vez y luego se para”.

Aquí tienes un escenario realista. Un fundador construye un prototipo con una herramienta IA, lo ejecuta local y entra con la misma pestaña del navegador. En producción, los usuarios abren la app en el móvil, cambian de red, dejan la pestaña en segundo plano y vuelven más tarde. Ahí es exactamente donde aparecen los problemas JWT: expiraciones cortas, diferencias de reloj y lógica de refresh que solo funciona en el camino feliz.

Cómo reproducirlo (sin adivinar)

Convierte los “cierres de sesión aleatorios” en una línea temporal. Pide a un usuario afectado la hora en que inició sesión y la hora en que se desconectó (su zona horaria importa). Luego recoge una petición fallida en el servidor (o en logs) y captura:

  • Hora del servidor cuando la petición fue rechazada (401)
  • Los valores exp e iat del token (decodificados en el servidor)
  • Si hubo un intento de refresh y si falló
  • Si el usuario tenía múltiples dispositivos o pestañas abiertas

Si puedes, compara el reloj del servidor con una fuente confiable y con tu máquina local. Unos pocos minutos de drift son suficientes para romper validaciones estrictas.

Las causas más probables

En prototipos, estos problemas reaparecen una y otra vez:

  • exp demasiado corto (como 5-15 minutos) y sin flujo de refresh fiable, por lo que los usuarios reciben cierres sorpresa.
  • Fallos por desfase de reloj: el backend comprueba exp e iat sin tolerancia y una máquina tiene la hora ligeramente distinta.
  • Errores en la rotación del refresh token: rotas refresh tokens pero no manejas correctamente el "reuso" del token, o sobrescribes el refresh almacenado en una carrera entre dos peticiones.

Ruta de corrección práctica

Las soluciones suelen ser directas si las atacas en orden.

Primero, añade una pequeña leeway al validar claims temporales (normalmente 30-120 segundos). Eso solo puede parar expiraciones falsas causadas por desfase de reloj.

Luego, haz el refresh fiable. Cuando un access token expira, realiza un único reintento de refresh y luego reenvía la petición original. Si múltiples peticiones alcanzan la expiración al mismo tiempo, asegúrate de que solo una haga el refresh y las demás esperen.

Finalmente, mejora el almacenamiento. Usa cookies seguras para refresh en web cuando sea posible (httpOnly, Secure, SameSite configurado correctamente), y evita tener refresh tokens largos en localStorage. En móvil, usa el almacenamiento seguro de la plataforma.

Si tu prototipo fue generado rápido y la auth es inestable, FixMyMess puede ejecutar una auditoría de código gratuita para señalar dónde la lógica de refresh, la rotación o los patrones de almacenamiento están fallando en uso real, y luego parchearlo para que quede listo para producción.

Lista rápida y siguientes pasos

Cuando los problemas de auth JWT parecen “aleatorios”, normalmente no lo son. Un token falla por un conjunto reducido de razones: los números dentro están mal, el reloj del servidor está desincronizado, el flujo de refresh está incompleto, o tu app almacena tokens de forma frágil.

Empieza con pruebas rápidas, no con conjeturas. Copia un token fallido, decodifícalo y anota exp, iat, iss, aud y el id/subject. Luego compáralo con lo que tu API valida en ese entorno.

Aquí tienes una lista corta que atrapa la mayoría de problemas de prototipo:

  • Decodifica un token que falla y confirma que exp está en el futuro y que iat no está “en el futuro” respecto al tiempo del servidor API.
  • Verifica la hora del API: comprueba el reloj del servidor, el tiempo del contenedor y la sincronización del hosting. Incluso unos minutos de drift pueden causar fallos repentinos.
  • Confirma la configuración de firma: asegúrate de que el secreto/clave privada es la correcta para este entorno (dev vs preview vs prod), y que no estás mezclando claves entre servicios.
  • Valida audience/issuer: aud e iss deben coincidir con lo que la API espera en cada entorno (especialmente en despliegues preview).
  • Prueba el comportamiento de refresh: el endpoint de refresh devuelve un nuevo access token de forma fiable, y la rotación no invalida accidentalmente a usuarios con sesión activa.

Después de eso, haz una prueba de extremo a extremo como un usuario real: inicia sesión, espera a que el access token expire, luego haz una llamada API y observa si la app refresca y reintenta. Si falla, busca estos patrones: refresh token no enviado (flags de cookie mal), refresh token sobrescrito durante rotación, o el cliente que nunca reintenta la petición original.

El almacenamiento es tu última comprobación rápida: evita poner refresh tokens en localStorage. Para apps web, una cookie HTTP-only es normalmente la opción más segura por defecto. Para móvil, usa el almacenamiento seguro de la plataforma.

Si tu prototipo generado por IA (Lovable, Bolt, v0, Cursor, Replit) tiene auth inestable y necesitas ponerlo en producción rápido, FixMyMess puede realizar una auditoría de código gratuita para identificar qué falla y luego reparar o reconstruir el flujo de forma segura.