Cómo hacer que recuperar la contraseña sea seguro en tu app
Cómo es un flujo seguro para recuperar la contraseña, los errores típicos con los enlaces en apps hechas con IA, cómo funciona en Supabase y cómo probarlo.
Read in English →
Un flujo seguro para recuperar la contraseña envía al correo de la cuenta un enlace de un solo uso y de vida corta, muestra el mismo mensaje exista o no ese correo, deja poner la contraseña nueva solo después de que el servidor verifica el enlace, y al terminar cierra las sesiones viejas y avisa al usuario del cambio. El enlace tiene que apuntar a tu dominio real, y las solicitudes tienen que estar limitadas para que nadie pueda llenar una bandeja de correos ni adivinar tokens.
En esta guía tienes el flujo paso a paso, los errores que más aparecen en apps hechas con herramientas de IA, cómo lo resuelve Supabase y qué te toca configurar a ti, y una prueba que puedes hacer en tu propia app.
¿Cómo es un flujo de recuperación seguro?
- El usuario pide recuperar la contraseña escribiendo su correo en una página pública.
- La app responde siempre igual: "Si existe una cuenta con ese correo, te enviamos un enlace". Mismo texto y mismo tiempo de respuesta, exista o no la cuenta. Si no, el formulario le cuenta a cualquiera qué correos están registrados.
- El servidor genera un token aleatorio, largo, ligado a esa cuenta, válido una sola vez y por poco tiempo, y guardado de forma segura (cifrado como una contraseña).
- El correo trae un enlace a tu dominio con ese token. La dirección base sale de tu configuración, nunca del encabezado host de la petición.
- La página de recuperación verifica el token en el servidor antes de mostrar el formulario. Un token inválido o ya usado recibe un error claro y la opción de pedir otro.
- La contraseña nueva se valida con las mismas reglas del registro, y se escribe dos veces.
- Después del cambio, el token queda anulado, se cierran las demás sesiones (o se le ofrece al usuario hacerlo) y llega un correo de "tu contraseña cambió" a la dirección de la cuenta.
- El usuario inicia sesión normalmente. La guía de OWASP desaconseja iniciar sesión automáticamente justo después de recuperar.
Si quieres más detalle, la referencia es la Forgot Password Cheat Sheet de OWASP.
¿Qué errores cometen las apps hechas con IA?
Estos se repiten cuando el flujo salió de un prompt:
- La página de recuperación no pide token. Llama a "actualizar contraseña" para el correo que venga en la URL o el formulario. Cualquiera puede cambiarle la contraseña a cualquiera. Es el más grave.
- El enlace apunta a localhost o a la vista previa, porque nadie actualizó la redirección para el dominio real. El usuario hace clic y cae en una página que no carga. En el redirect del login de Lovable en tu dominio explicamos cómo corregir esos ajustes.
- Mensajes de "no hay ninguna cuenta con ese correo", que revelan quién usa tu app.
- Sin límite de solicitudes, así que un script puede mandar cientos de correos a una dirección o martillar el endpoint de verificación.
- Las sesiones siguen vivas. Si alguien había robado una sesión, sigue funcionando después de que el dueño cambia la contraseña.
- Scripts de terceros en la página de recuperación (analítica, widgets de chat) que pueden ver la URL con el token. Pon una política
no-referreren esa página y déjala sin rastreadores.
¿Cómo funciona la recuperación en Supabase?
Supabase Auth te da la mayoría de las piezas seguras; tú las conectas:
resetPasswordForEmail(email, { redirectTo })envía el correo. La documentación de Supabase aclara que no revela si la cuenta existe: para correos desconocidos no envía nada, pero la llamada responde sin error. Muestra tu mensaje neutro en cualquier caso.redirectTotiene que estar en Redirect URLs dentro de los ajustes de autenticación del proyecto. Si tu dominio real no está ahí, el enlace vuelve a tu Site URL o falla.- Con el flujo PKCE, la plantilla del correo apunta a una ruta de tu sitio (por ejemplo
/auth/confirm) que llama averifyOtpcon el hash del token y después lleva al usuario a tu página de "nueva contraseña". Esa página solo debe funcionar con una sesión de recuperación activa. - En esa página,
updateUser({ password })guarda la contraseña nueva. Las versiones recientes desupabase-jstambién aceptan la contraseña actual para los cambios normales (fuera de la recuperación).
Lo que sigue siendo tarea tuya:
- El envío de correos. El servicio de correo incluido en Supabase es limitado (su documentación habla de 2 correos por hora) y está pensado para pruebas. Configura un SMTP propio antes de lanzar. Mira Supabase no envía el correo de confirmación.
- Las reglas de contraseña en los ajustes de autenticación (largo mínimo, caracteres requeridos) y, si tu plan lo incluye, la protección contra contraseñas filtradas.
- Cerrar las demás sesiones después del cambio, por ejemplo con
signOut({ scope: 'others' }). - El aviso de "tu contraseña cambió", si las plantillas de correo de tu proyecto no lo cubren.
- Un CAPTCHA en el formulario si ves abuso; Supabase Auth lo admite.
¿Cómo pruebo mi flujo de recuperación?
Usa una cuenta de prueba tuya, de preferencia en un entorno de pruebas.
- Pide recuperar la contraseña de un correo registrado y de uno que no lo está. El mensaje en pantalla debe ser idéntico.
- Abre el correo y revisa que el enlace vaya a tu dominio real por HTTPS.
- Usa el enlace y pon una contraseña nueva. Luego vuelve atrás e intenta usar el mismo enlace. Debe rechazarse.
- Pide otro enlace, espera más que el vencimiento configurado en tus ajustes y pruébalo. Debe rechazarse.
- Abre la página de recuperación sin token, o con uno inventado. No debe mostrar un formulario que funcione.
- Antes de recuperar, inicia sesión en otro navegador. Después de cambiar la contraseña, recarga ese navegador: la sesión vieja debe haberse cerrado, o te deben haber ofrecido cerrarla.
- Envía el formulario muchas veces seguidas. Debes chocar con un límite.
- Revisa que llegó el aviso de "contraseña cambiada".
Si en el paso 5 puedes poner una contraseña, detente y arregla eso antes que nada.
¿Cómo se conecta esto con el resto de la seguridad del login?
La recuperación es una puerta a la cuenta, así que vale lo que vale el correo que hay detrás. Dos cosas refuerzan todo el sistema:
- Doble factor. Si el usuario tiene un segundo factor, un correo de recuperación por sí solo no debería bastar para tomar la cuenta. Mira autenticación de dos factores en tu app.
- Trata el cambio de correo como el de contraseña. Cambiar el correo de la cuenta debería pedir la contraseña actual o un inicio de sesión reciente, y avisar a la dirección anterior. Si no, alguien con una sesión robada cambia el correo y después recupera la contraseña.
Las fallas de autenticación están en el puesto siete del OWASP Top 10; nuestro resumen del OWASP Top 10 para apps hechas con IA las pone en contexto.
Respuestas rápidas
¿El formulario debería decir "correo no encontrado"? No. Muestra siempre el mismo mensaje neutro, para que no sirva para averiguar quién tiene cuenta.
¿Cuánto debe durar un enlace de recuperación? Lo suficientemente poco como para que un correo viejo en una bandeja no sirva: de minutos a una hora es lo habitual. Usa el ajuste de vencimiento de tu proveedor de autenticación.
¿Puedo usar preguntas de seguridad? OWASP desaconseja depender solo de ellas; las respuestas suelen adivinarse o encontrarse en internet.
¿Inicio sesión automáticamente después de recuperar? Mejor no. Manda al usuario al inicio de sesión normal, que además confirma que la contraseña nueva funciona.
¿No sabes si tu flujo pasa la prueba?
El chequeo de salud gratis revisa lo que tu app publicada expone desde afuera. Si quieres que una persona revise en el código tu registro, inicio de sesión y recuperación, nuestra revisión de seguridad de Supabase lo cubre, o puedes hacernos una pregunta.


