Solicitar Restablecimiento de Contraseña
Solicita un enlace de restablecimiento de contraseña que se enviará por correo electrónico. El enlace es válido por 10 minutos.
Información General
Este endpoint permite a los usuarios solicitar un restablecimiento de contraseña cuando la han olvidado. El sistema genera un token único y envía un enlace de restablecimiento al correo electrónico del usuario.
/auth/forgot-passwordGenera un token de restablecimiento y envía un enlace al email del usuario
📋 Parámetros
emailstringrequeridoDirección de correo electrónico del usuario registrado
📤 Respuesta
{
"code": 1002,
"message": "Password reset link sent successfully",
"data": {
"status": "pending"
}
}Validaciones
Email
- Debe tener formato válido:
usuario@dominio.com - Cumplir con regex:
/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/ - No puede estar vacío
Respuestas
Respuesta Exitosa
Enlace Enviado Exitosamente
Código 1002 - Enlace de restablecimiento enviado al email.
{
"code": 1002,
"message": "Password reset link sent successfully",
"data": {
"status": "pending"
}
}
Comportamiento del sistema:
- Se genera un token UUID único
- Se guarda en Redis:
reset:{token}= email (TTL: 10 minutos) - Se envía email con plantilla
RESET_PASSWORD - Enlace generado:
https://www.swapbits.site/api/auth/reset-password?token={UUID} - Se registra evento de auditoría:
password_reset_request
Errores
Email Inválido o Faltante
Código 4006 - Datos faltantes o formato inválido.
{
"code": 4006,
"message": "Missing or invalid data"
}
Causas posibles:
- Campo
emailvacío o no enviado - Formato de email inválido (no cumple regex)
Proceso Interno del Sistema
Flujo de Operación
sequenceDiagram
participant Client
participant API
participant Redis
participant EmailService
Client->>API: POST /auth/forgot-password
API->>API: Validar formato email
alt Email inválido
API->>Client: 400 (code: 4006)
end
API->>Redis: Generar UUID único
API->>Redis: Guardar reset:{token} = email (TTL: 10 min)
API->>EmailService: Enviar email con link
EmailService-->>Client: Email enviado
API->>Client: 200 (code: 1002)
Note over Client,EmailService: Usuario recibe email con enlace válido por 10 minutos
Pasos del Proceso
- Validación de Email: Verifica formato con regex
/^[^\s@]+@[^\s@]+\.[^\s@]+$/ - Búsqueda de Usuario: Verifica que el email exista en la base de datos
- Generación de Token: Crea un UUID v4 único para el proceso de reset
- Almacenamiento en Redis:
- Clave:
reset:{token} - Valor: email del usuario
- TTL: 10 minutos (600,000 ms)
- Clave:
- Envío de Email: Email con plantilla
RESET_PASSWORDque contiene el enlace - Enlace Generado:
https://www.swapbits.site/api/auth/reset-password?token={UUID}
Ejemplos de Implementación
JavaScript/TypeScript
async function requestPasswordReset(email) {
try {
const response = await fetch('https://api.swapbits.co/auth/forgot-password', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
const result = await response.json();
if (result.code === 1002) {
console.log('Enlace de restablecimiento enviado');
// IMPORTANTE: Mostrar mensaje genérico por seguridad
return {
success: true,
message: 'Si el correo está registrado, recibirás un enlace de restablecimiento'
};
} else if (result.code === 4006) {
console.error('Email inválido');
return {
success: false,
error: 'EMAIL_INVALID',
message: 'Por favor ingresa un email válido'
};
}
return { success: false, error: result.message };
} catch (error) {
console.error('Error de red:', error);
return { success: false, error: error.message };
}
}
// Validación en frontend
const validateEmail = (email) => {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
};
// Uso
const email = 'usuario@ejemplo.com';
if (validateEmail(email)) {
const result = await requestPasswordReset(email);
if (result.success) {
alert(result.message);
} else {
alert(result.message);
}
} else {
alert('Por favor ingresa un email válido');
}
TypeScript con Hook de React
import { useState } from 'react';
import axios from 'axios';
interface ForgotPasswordState {
loading: boolean;
error: string | null;
success: boolean;
}
export function useForgotPassword() {
const [state, setState] = useState<ForgotPasswordState>({
loading: false,
error: null,
success: false
});
const requestReset = async (email: string) => {
// Validar email antes de enviar
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
setState({
loading: false,
error: 'Formato de email inválido',
success: false
});
return false;
}
setState({ loading: true, error: null, success: false });
try {
const response = await axios.post(
'https://api.swapbits.co/auth/forgot-password',
{ email }
);
if (response.data.code === 1002) {
setState({ loading: false, error: null, success: true });
return true;
} else {
throw new Error('Respuesta inesperada del servidor');
}
} catch (error: any) {
const errorMessage = error.response?.data?.message || 'Error al solicitar restablecimiento';
setState({ loading: false, error: errorMessage, success: false });
return false;
}
};
const reset = () => {
setState({ loading: false, error: null, success: false });
};
return { ...state, requestReset, reset };
}
// Componente de ejemplo con cooldown
function ForgotPasswordForm() {
const [email, setEmail] = useState('');
const [cooldown, setCooldown] = useState(0);
const { loading, error, success, requestReset } = useForgotPassword();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (cooldown > 0) return;
const isSuccess = await requestReset(email);
if (isSuccess) {
// Iniciar cooldown de 60 segundos
setCooldown(60);
const interval = setInterval(() => {
setCooldown(prev => {
if (prev <= 1) {
clearInterval(interval);
return 0;
}
return prev - 1;
});
}, 1000);
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="correo@ejemplo.com"
required
/>
<button type="submit" disabled={loading || cooldown > 0}>
{loading ? 'Enviando...' : cooldown > 0 ? `Espera ${cooldown}s` : 'Restablecer Contraseña'}
</button>
{success && (
<p className="success">
Si el correo está registrado, recibirás un enlace de restablecimiento
</p>
)}
{error && <p className="error">Error: {error}</p>}
</form>
);
}
Python
import requests
import re
from typing import Dict, Any
def validate_email(email: str) -> bool:
"""Validar formato de email"""
email_regex = r'^[^\s@]+@[^\s@]+\.[^\s@]+$'
return bool(re.match(email_regex, email))
def request_password_reset(email: str) -> Dict[str, Any]:
"""
Solicita restablecimiento de contraseña
Args:
email: Dirección de correo electrónico del usuario
Returns:
Respuesta del servidor con código y mensaje
Raises:
ValueError: Si el email es inválido
requests.RequestException: Si hay error de red
"""
# Validar formato de email
if not validate_email(email):
return {
'success': False,
'error': 'EMAIL_INVALID',
'message': 'Formato de email inválido'
}
url = 'https://api.swapbits.co/auth/forgot-password'
data = {'email': email}
try:
response = requests.post(url, json=data, timeout=10)
result = response.json()
if result['code'] == 1002:
print("Enlace de restablecimiento enviado")
return {
'success': True,
'message': 'Si el correo está registrado, recibirás un enlace de restablecimiento'
}
elif result['code'] == 4006:
return {
'success': False,
'error': 'EMAIL_INVALID',
'message': 'Email inválido o faltante'
}
else:
return {
'success': False,
'error': 'UNKNOWN',
'message': result.get('message', 'Error desconocido')
}
except requests.exceptions.Timeout:
return {
'success': False,
'error': 'TIMEOUT',
'message': 'Tiempo de espera agotado'
}
except requests.exceptions.RequestException as e:
return {
'success': False,
'error': 'NETWORK_ERROR',
'message': str(e)
}
# Ejemplo de uso con interfaz CLI
def main():
email = input("Ingresa tu email: ")
print(f"\nSolicitando restablecimiento para: {email}")
result = request_password_reset(email)
if result['success']:
print(f"\nÉxito: {result['message']}")
print("Revisa tu bandeja de entrada y carpeta de spam")
else:
print(f"\nError: {result['message']}")
if __name__ == '__main__':
main()
cURL
# Solicitud básica
curl -X POST \
'https://api.swapbits.co/auth/forgot-password' \
-H 'Content-Type: application/json' \
-d '{
"email": "usuario@ejemplo.com"
}'
# Script bash con validación y manejo de respuesta
#!/bin/bash
EMAIL="usuario@ejemplo.com"
# Validar formato de email
if ! echo "$EMAIL" | grep -qE '^[^\s@]+@[^\s@]+\.[^\s@]+$'; then
echo "Error: Formato de email inválido"
exit 1
fi
# Hacer request
response=$(curl -s -X POST \
'https://api.swapbits.co/auth/forgot-password' \
-H 'Content-Type: application/json' \
-d "{\"email\": \"$EMAIL\"}")
code=$(echo $response | jq -r '.code')
message=$(echo $response | jq -r '.message')
case $code in
1002)
echo "Éxito: Enlace de restablecimiento enviado"
echo "Revisa tu email: $EMAIL"
;;
4006)
echo "Error: Email inválido o faltante"
;;
*)
echo "Error: $message"
;;
esac
Consideraciones de Seguridad
Protección Contra Enumeración de Usuarios
Comportamiento de seguridad crítico:
El endpoint implementa una protección importante: siempre devuelve la misma respuesta exitosa (código 1002), incluso si el email no está registrado en el sistema.
Comportamiento:
- Email registrado: Envía enlace real + respuesta
1002 - Email NO registrado: NO envía email + respuesta
1002(misma respuesta)
Propósito: Prevenir que atacantes determinen qué emails están registrados en el sistema mediante prueba y error.
Implementación Correcta en Frontend
Buena práctica - Siempre mostrar mensaje genérico:
async function handleForgotPassword(email) {
const result = await requestPasswordReset(email);
// SIEMPRE mostrar este mensaje, sin importar si el email existe
showMessage('Si el correo está registrado, recibirás un enlace de restablecimiento');
// ❌ NO mostrar mensajes como:
// - "Email no encontrado"
// - "Usuario no existe"
// - "Email enviado" (demasiado específico)
}
Prevención de Abuso
Recomendaciones para implementar en frontend:
- Rate Limiting Local: Limitar solicitudes por IP/sesión (máximo 3 por hora)
- Cooldown Timer: Deshabilitar botón por 60 segundos después de cada envío
- CAPTCHA: Implementar después de 2-3 intentos fallidos
- Validación Previa: Validar formato de email antes de enviar request
// Ejemplo de cooldown
const [cooldown, setCooldown] = useState(0);
const [requestCount, setRequestCount] = useState(0);
const handleRequest = async (email) => {
if (cooldown > 0) {
alert(`Espera ${cooldown} segundos`);
return;
}
// Verificar límite de intentos
if (requestCount >= 3) {
alert('Has alcanzado el límite de intentos. Intenta más tarde.');
return;
}
await requestPasswordReset(email);
setRequestCount(prev => prev + 1);
// Iniciar cooldown de 60 segundos
setCooldown(60);
const interval = setInterval(() => {
setCooldown(prev => {
if (prev <= 1) {
clearInterval(interval);
return 0;
}
return prev - 1;
});
}, 1000);
};
Características del Sistema
Token de Restablecimiento
Especificaciones técnicas:
- Tipo: UUID v4 único
- Almacenamiento: Redis con clave
reset:{token} - Valor almacenado: Email del usuario
- TTL: 10 minutos (600,000 ms)
- Uso: Un solo uso por token
- Enlace generado:
https://www.swapbits.site/api/auth/reset-password?token={UUID}
Expiración y Límites
Límites de tiempo:
- Token válido por 10 minutos exactos
- Después de 10 minutos, el token se elimina automáticamente de Redis
- Si el token expira, el usuario debe solicitar un nuevo enlace
- Cada solicitud genera un nuevo token único
Email de Restablecimiento
Detalles del email:
- Plantilla:
RESET_PASSWORD(configurada en EmailService) - Contenido: Enlace con token incluido
- Remitente: Sistema SwapBits
- Evento de auditoría:
password_reset_request - Log: Se registra cada solicitud para auditoría
Flujo Completo del Usuario
graph TD
A[Usuario olvida contraseña] --> B[Ir a Forgot Password]
B --> C[Ingresar email]
C --> D{Email válido?}
D -->|No| E[Error 4006 - Email inválido]
D -->|Sí| F[POST /auth/forgot-password]
F --> G[Sistema genera UUID]
G --> H[Guardar en Redis - TTL 10min]
H --> I[Enviar email con enlace]
I --> J[Respuesta 1002]
J --> K[Usuario recibe email]
K --> L[Clic en enlace]
L --> M[GET /auth/reset-password?token=xxx]
M --> N[Formulario de nueva contraseña]
N --> O[POST /auth/reset-password]
O --> P[Contraseña actualizada]
Notas Importantes
Información Clave
Puntos importantes a recordar:
- Expiración del Token: El enlace es válido por exactamente 10 minutos
- Token de Uso Único: Cada token solo puede ser usado una vez
- Respuesta Unificada: Siempre retorna código 1002 por seguridad
- Email Template: Plantilla
RESET_PASSWORDdel EmailService - URL del Enlace:
https://www.swapbits.site/api/auth/reset-password?token={UUID} - Redis Storage: Clave
reset:{token}con email como valor - Auditoría: Evento
password_reset_requestregistrado en logs - Sin Autenticación: No requiere estar autenticado para usar este endpoint
Códigos de Respuesta
| Código | HTTP Status | Descripción |
|---|---|---|
| 1002 | 200 | Enlace de restablecimiento enviado correctamente |
| 4006 | 400 | Email faltante o formato inválido |
Probar esta API
¿Quieres probar este endpoint de forma interactiva con tus propios datos?
Enlaces Relacionados
- Restablecer Contraseña - Completar el proceso de restablecimiento
- Solicitar Cambio de Contraseña - Solicitar cambio estando autenticado
- Login - Iniciar sesión después de restablecer
- Registro v1 - Crear nueva cuenta