Saltar al contenido principal

Reenviar Código de Registro

Endpoint para solicitar el reenvío de un nuevo código de verificación de 6 dígitos si el código anterior expiró o no fue recibido durante el proceso de registro.


Información General

Este endpoint permite a los usuarios solicitar un nuevo código de verificación durante el registro v1 sin tener que reiniciar todo el proceso. Implementa un sistema de cooldown para prevenir abuso.

POST/auth/register/resend?token={token}

Genera y envía un nuevo código de verificación al email del usuario, invalidando el código anterior

📋 Parámetros

tokenstringrequerido

Token de sesión UUID obtenido en el Paso 1 del registro

📤 Respuesta

{
"code": 1010,
"message": "Verification code sent successfully",
"data": {
  "cooldown": 30
}
}

Funcionamiento

Sistema de Cooldown

Para prevenir spam y abuso, el sistema implementa un cooldown de 30 segundos entre solicitudes de reenvío:

  • Primera solicitud: Envía código inmediatamente
  • Solicitudes subsecuentes: Deben esperar 30 segundos desde la última solicitud
  • El cooldown se reinicia con cada reenvío exitoso
  • El cooldown es específico por token de sesión

Gestión de Códigos

  • Cada nuevo código invalida el código anterior
  • El nuevo código tiene una validez de 5 minutos
  • Los códigos son de 6 dígitos numéricos
  • Se almacenan en Redis con TTL automático

Validaciones

Token de Sesión

  • Debe ser un token UUID válido del Paso 1
  • Debe existir en Redis con datos de sesión válidos
  • No debe haber expirado (10 minutos desde el Paso 1)
  • Debe contener información del email del usuario

Sistema de Rate Limiting

  • Cooldown de 30 segundos entre reenvíos
  • Se verifica mediante clave en Redis: resend_cooldown:register:{token}
  • Retorna error 429 si el cooldown está activo

Respuestas

Respuesta Exitosa

Código Reenviado Exitosamente

Protección anti-spam de 30 segundos

El sistema implementa un período de espera obligatorio de 30 segundos entre cada solicitud de reenvío para:

  • Prevenir abuso del servicio de email
  • Reducir costos de envío
  • Evitar saturación de la bandeja del usuario
  • Proteger contra ataques de fuerza bruta

Validación de Token

Requisitos del token:

  • Debe ser un UUID válido
  • Debe estar asociado a una sesión activa
  • No debe haber expirado (10 minutos de vida útil)
  • Debe corresponder a un proceso de registro iniciado

Respuestas del Sistema

Código Reenviado Exitosamente

Código 1010 - Nuevo código de verificación enviado.

{
"code": 1010,
"message": "Verification code sent successfully",
"data": {
"cooldown": 30
}
}

Comportamiento del sistema:

  • Se genera un nuevo código de 6 dígitos
  • Se invalida el código anterior
  • Se envía email con el nuevo código
  • Se activa cooldown de 30 segundos
  • El nuevo código expira en 5 minutos

Cooldown Activo

Código 4030 - Debes esperar antes de solicitar otro código.

{
"code": 4030,
"message": "Please wait 30 seconds before requesting another code"
}

El usuario debe esperar el tiempo de cooldown antes de poder solicitar un nuevo código.

Token Inválido o Expirado

Código 4015 - El token de sesión no es válido.

{
"code": 4015,
"message": "Invalid session token"
}

Posibles causas:

  • Token expirado (más de 10 minutos)
  • Token malformado o corrupto
  • Sesión no encontrada en Redis
  • Token ya utilizado completamente

Datos Faltantes

Código 4006 - Falta el parámetro token requerido.

{
"code": 4006,
"message": "Missing required data"
}

Ejemplos de Implementación

JavaScript/TypeScript

const resendVerificationCode = async (token) => {
try {
const response = await fetch(
`https://api.swapbits.co/auth/register/resend?token=${token}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}
);

const result = await response.json();

switch (result.code) {
case 1010:
console.log('✓ Nuevo código enviado exitosamente');
console.log(`⏳ Cooldown: ${result.data.cooldown} segundos`);

// Deshabilitar botón de reenvío temporalmente
const resendButton = document.getElementById('resend-button');
resendButton.disabled = true;
resendButton.textContent = `Espera ${result.data.cooldown}s`;

// Reactivar después del cooldown
setTimeout(() => {
resendButton.disabled = false;
resendButton.textContent = 'Reenviar código';
}, result.data.cooldown * 1000);

return { success: true, cooldown: result.data.cooldown };

case 4030:
console.error('❌ Debes esperar antes de solicitar otro código');
return { success: false, error: 'COOLDOWN_ACTIVE' };

case 4015:
console.error('❌ Token inválido o expirado');
return {
success: false,
error: 'INVALID_TOKEN',
message: 'Tu sesión ha expirado, por favor inicia el proceso nuevamente'
};

default:
console.error('❌ Error desconocido:', result.message);
return { success: false, error: result.message };
}
} catch (error) {
console.error('❌ Error de red:', error);
return { success: false, error: error.message };
}
};

// Uso con control de cooldown en UI
const token = sessionStorage.getItem('registrationToken');
resendVerificationCode(token);

Python

import requests
import time

def resend_verification_code(token: str) -> dict:
"""
Reenviar código de verificación con manejo de cooldown

Args:
token: Token UUID de sesión del registro

Returns:
dict: Resultado de la operación con success, cooldown o error
"""
url = f'https://api.swapbits.co/auth/register/resend?token={token}'

try:
response = requests.post(url, timeout=10)
result = response.json()

if result['code'] == 1010:
print(f"✓ Código reenviado exitosamente")
print(f"⏳ Cooldown: {result['data']['cooldown']} segundos")
return {
'success': True,
'cooldown': result['data']['cooldown'],
'message': 'Revisa tu email para el nuevo código'
}
elif result['code'] == 4030:
print("❌ Cooldown activo, debes esperar")
return {
'success': False,
'error': 'COOLDOWN_ACTIVE',
'message': 'Espera 30 segundos antes de solicitar otro código'
}
elif result['code'] == 4015:
print("❌ Token inválido o expirado")
return {
'success': False,
'error': 'INVALID_TOKEN',
'message': 'Tu sesión ha expirado'
}
else:
return {'success': False, 'error': result['message']}

except requests.exceptions.Timeout:
return {'success': False, 'error': 'REQUEST_TIMEOUT'}
except requests.exceptions.RequestException as e:
return {'success': False, 'error': str(e)}

# Ejemplo de uso con retry automático
def resend_with_retry(token: str, max_retries: int = 3) -> dict:
"""Reintentar reenvío automáticamente respetando cooldown"""
for attempt in range(max_retries):
result = resend_verification_code(token)

if result['success']:
return result

if result.get('error') == 'COOLDOWN_ACTIVE':
print(f"Intento {attempt + 1}/{max_retries}: Esperando cooldown...")
time.sleep(30)
continue

# Si es otro error, no reintentar
return result

return {'success': False, 'error': 'MAX_RETRIES_EXCEEDED'}

# Uso
token = 'your-session-token-here'
result = resend_verification_code(token)

cURL

# Solicitar reenvío de código
curl -X POST 'https://api.swapbits.co/auth/register/resend?token=550e8400-e29b-41d4-a716-446655440000' \
-H 'Content-Type: application/json'

# Respuesta exitosa
# {
# "code": 1010,
# "message": "Verification code sent successfully",
# "data": {
# "cooldown": 30
# }
# }

Flujo de Usuario Recomendado

1. Interfaz de Usuario

// Implementación recomendada en el frontend
class CodeResendManager {
constructor(token) {
this.token = token;
this.cooldownActive = false;
this.cooldownRemaining = 0;
}

async resendCode() {
if (this.cooldownActive) {
console.warn('Cooldown activo, por favor espera');
return { success: false, error: 'COOLDOWN_ACTIVE' };
}

const result = await resendVerificationCode(this.token);

if (result.success) {
this.startCooldown(result.cooldown);
}

return result;
}

startCooldown(seconds) {
this.cooldownActive = true;
this.cooldownRemaining = seconds;

const interval = setInterval(() => {
this.cooldownRemaining--;
this.updateUI();

if (this.cooldownRemaining <= 0) {
clearInterval(interval);
this.cooldownActive = false;
this.updateUI();
}
}, 1000);
}

updateUI() {
const button = document.getElementById('resend-button');
const message = document.getElementById('cooldown-message');

if (this.cooldownActive) {
button.disabled = true;
button.textContent = `Reenviar (${this.cooldownRemaining}s)`;
message.textContent = `Puedes solicitar un nuevo código en ${this.cooldownRemaining} segundos`;
} else {
button.disabled = false;
button.textContent = 'Reenviar código';
message.textContent = '¿No recibiste el código?';
}
}
}

// Uso
const manager = new CodeResendManager(sessionStorage.getItem('registrationToken'));
document.getElementById('resend-button').addEventListener('click', () => {
manager.resendCode();
});

2. Manejo de Errores

const handleResendErrors = async (token) => {
const result = await resendVerificationCode(token);

if (!result.success) {
switch (result.error) {
case 'COOLDOWN_ACTIVE':
showNotification('Por favor espera antes de solicitar otro código', 'warning');
break;

case 'INVALID_TOKEN':
showNotification('Tu sesión ha expirado. Reinicia el proceso de registro.', 'error');
// Redirigir al inicio del registro
window.location.href = '/register';
break;

case 'REQUEST_TIMEOUT':
showNotification('Error de conexión. Verifica tu internet.', 'error');
break;

default:
showNotification('Error al reenviar código. Intenta nuevamente.', 'error');
}
} else {
showNotification('Código enviado exitosamente. Revisa tu email.', 'success');
}
};

Casos de Uso Comunes

Código No Recibido

Situación: El usuario no recibe el email con el código.

Solución:

  1. Verificar carpeta de spam/correo no deseado
  2. Esperar 1-2 minutos (entrega puede demorar)
  3. Usar endpoint de reenvío después del cooldown
  4. Verificar que el email sea correcto

Código Expirado

Situación: El código de 6 dígitos ya expiró (5 minutos).

Solución:

  1. Solicitar nuevo código con este endpoint
  2. El código anterior se invalida automáticamente
  3. Usar el nuevo código dentro de 5 minutos

Múltiples Intentos

Situación: Usuario intenta reenviar múltiples veces rápidamente.

Comportamiento:

  • Sistema bloquea con código 4030
  • Debe esperar 30 segundos obligatorios
  • Protege contra spam y abuso
  • Frontend debe deshabilitar botón durante cooldown

TypeScript con UI y Cooldown Timer

import { useState, useEffect } from 'react';

interface ResendResponse {
success: boolean;
cooldown?: number;
error?: string;
message?: string;
}

const ResendCodeButton = ({ token }: { token: string }) => {
const [loading, setLoading] = useState(false);
const [cooldown, setCooldown] = useState(0);
const [message, setMessage] = useState<string | null>(null);

// Countdown timer
useEffect(() => {
if (cooldown > 0) {
const timer = setTimeout(() => {
setCooldown(cooldown - 1);
}, 1000);
return () => clearTimeout(timer);
}
}, [cooldown]);

const resendCode = async (): Promise<ResendResponse> => {
try {
const response = await fetch(
`https://api.swapbits.co/auth/register/resend?token=${token}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
}
);

const result = await response.json();

switch (result.code) {
case 1010:
return {
success: true,
cooldown: result.data.cooldown,
message: 'Nuevo código enviado a tu email'
};
case 4030:
return {
success: false,
error: 'COOLDOWN_ACTIVE',
message: 'Debes esperar antes de solicitar otro código'
};
case 4015:
return {
success: false,
error: 'SESSION_EXPIRED',
message: 'Sesión expirada. Reinicia el registro.'
};
default:
return {
success: false,
error: 'UNKNOWN',
message: result.message
};
}
} catch (error) {
return {
success: false,
error: 'NETWORK_ERROR',
message: 'Error de conexión'
};
}
};

const handleResend = async () => {
setLoading(true);
setMessage(null);

const result = await resendCode();

if (result.success && result.cooldown) {
setCooldown(result.cooldown);
setMessage(result.message || 'Código reenviado');
} else {
setMessage(result.message || 'Error al reenviar código');

// Redirigir si la sesión expiró
if (result.error === 'SESSION_EXPIRED') {
setTimeout(() => {
window.location.href = '/register';
}, 2000);
}
}

setLoading(false);
};

const isDisabled = loading || cooldown > 0;

return (
<div className="resend-container">
<button
onClick={handleResend}
disabled={isDisabled}
className="resend-button"
>
{loading ? (
'Enviando...'
) : cooldown > 0 ? (
`Espera ${cooldown}s`
) : (
'Reenviar código'
)}
</button>

{message && (
<div className={`message ${message.includes('Error') ? 'error' : 'success'}`}>
{message}
</div>
)}
</div>
);
};

export default ResendCodeButton;

cURL con Scripts

# Reenvío básico
curl -X POST \
'https://api.swapbits.co/auth/register/resend?token=550e8400-e29b-41d4-a716-446655440000' \
-H 'Content-Type: application/json'
# Script bash con manejo de cooldown
#!/bin/bash

TOKEN="550e8400-e29b-41d4-a716-446655440000"

resend_code() {
response=$(curl -s -X POST \
"https://api.swapbits.co/auth/register/resend?token=$TOKEN" \
-H 'Content-Type: application/json')

code=$(echo $response | jq -r '.code')
message=$(echo $response | jq -r '.message')

case $code in
1010)
cooldown=$(echo $response | jq -r '.data.cooldown')
echo "Código reenviado exitosamente"
echo "Espera $cooldown segundos antes de solicitar otro"
return 0
;;
4030)
echo "Error: Debes esperar 30 segundos antes de solicitar otro código"
return 1
;;
4015)
echo "Error: Sesión expirada - Reinicia el registro"
return 2
;;
*)
echo "Error: $message"
return 3
;;
esac
}

# Intentar reenvío con manejo de cooldown
echo "Solicitando reenvío de código..."
resend_code

exit_code=$?

if [ $exit_code -eq 1 ]; then
echo "Esperando 30 segundos por cooldown..."
sleep 30
echo "Reintentando..."
resend_code
fi

Flujo de Trabajo

graph TD
A[POST /auth/register/resend] --> B{Token existe?}
B -->|No| C[Error 4006/4015]
B -->|Sí| D{Cooldown activo?}
D -->|Sí| E[Error 4030 - Esperar]
D -->|No| F[Extraer email de sesión]
F --> G[Generar nuevo código 6 dígitos]
G --> H[Guardar código en Redis - 5 min]
H --> I[Invalidar código anterior]
I --> J[Enviar email con nuevo código]
J --> K[Establecer cooldown 30s]
K --> L[Retornar éxito 1010]

Mejores Prácticas

Para Desarrolladores Frontend

// Sistema de gestión de reenvío con UI feedback
class CodeResendManager {
constructor(token) {
this.token = token;
this.cooldownEndTime = null;
this.cooldownInterval = null;
}

async resend() {
if (this.isInCooldown()) {
return {
success: false,
error: 'LOCAL_COOLDOWN',
message: `Espera ${this.getRemainingCooldown()} segundos`
};
}

try {
const response = await fetch(
`https://api.swapbits.co/auth/register/resend?token=${this.token}`,
{ method: 'POST', headers: { 'Content-Type': 'application/json' } }
);

const result = await response.json();

if (result.code === 1010) {
// Iniciar cooldown local
this.startCooldown(result.data.cooldown);
return { success: true, cooldown: result.data.cooldown };
}

return { success: false, error: result.code, message: result.message };
} catch (error) {
return { success: false, error: 'NETWORK', message: error.message };
}
}

startCooldown(seconds) {
this.cooldownEndTime = Date.now() + (seconds * 1000);

// Actualizar UI cada segundo
this.cooldownInterval = setInterval(() => {
const remaining = this.getRemainingCooldown();

if (remaining <= 0) {
this.clearCooldown();
this.onCooldownEnd?.();
} else {
this.onCooldownTick?.(remaining);
}
}, 1000);
}

clearCooldown() {
if (this.cooldownInterval) {
clearInterval(this.cooldownInterval);
this.cooldownInterval = null;
}
this.cooldownEndTime = null;
}

isInCooldown() {
return this.cooldownEndTime && Date.now() < this.cooldownEndTime;
}

getRemainingCooldown() {
if (!this.cooldownEndTime) return 0;
return Math.ceil((this.cooldownEndTime - Date.now()) / 1000);
}

// Callbacks opcionales para actualizar UI
onCooldownTick = null;
onCooldownEnd = null;
}

// Uso
const manager = new CodeResendManager(token);

manager.onCooldownTick = (seconds) => {
document.getElementById('resend-btn').textContent = `Espera ${seconds}s`;
document.getElementById('resend-btn').disabled = true;
};

manager.onCooldownEnd = () => {
document.getElementById('resend-btn').textContent = 'Reenviar código';
document.getElementById('resend-btn').disabled = false;
};

// Reenviar código
const result = await manager.resend();
if (result.success) {
showNotification('Código reenviado. Revisa tu email.');
}

Consideraciones de Seguridad

Protección contra Abuso

Medidas implementadas:

  • Cooldown de 30 segundos entre reenvíos
  • Los códigos expiran en 5 minutos
  • Cada nuevo código invalida el anterior
  • La sesión completa expira en 10 minutos
  • Rate limiting por token de sesión

Recomendaciones adicionales:

  • Implementar rate limiting por IP en el servidor
  • Considerar incrementar el cooldown después de múltiples reenvíos
  • Registrar patrones sospechosos de reenvíos excesivos
  • Implementar CAPTCHA después de cierto número de reenvíos

Manejo de Sesiones

Límites temporales:

  • Token de sesión: 10 minutos desde el Paso 1
  • Código de verificación: 5 minutos desde la emisión
  • Cooldown de reenvío: 30 segundos

Comportamiento:

  • Si la sesión expira, el usuario debe reiniciar desde el Paso 1
  • Los códigos antiguos se invalidan automáticamente al solicitar reenvío
  • El cooldown se reinicia con cada reenvío exitoso

Notas Importantes

Cuándo Usar Este Endpoint

Usa este endpoint cuando:

  • El código de verificación no llega al email
  • El código expiró (5 minutos)
  • El usuario ingresó mal el email y no puede acceder al código
  • Se sospecha que el email fue filtrado como spam

No uses este endpoint para:

  • Reiniciar el proceso de registro (usa /auth/register nuevamente)
  • Cambiar el email de registro (debe reiniciarse todo el proceso)
  • Verificar si un código es válido (usa /auth/register/verify)

Probar esta API

¿Quieres probar este endpoint de forma interactiva con tus propios datos?


Enlaces Relacionados