# RF-USER-003: Cambio de Email ## Identificacion | Campo | Valor | |-------|-------| | **ID** | RF-USER-003 | | **Modulo** | MGN-002 | | **Nombre Modulo** | Users - Gestion de Usuarios | | **Prioridad** | P1 | | **Complejidad** | Media | | **Estado** | Aprobado | | **Autor** | System | | **Fecha** | 2025-12-05 | --- ## Descripcion El sistema debe permitir a los usuarios cambiar su direccion de email de forma segura, requiriendo verificacion del nuevo email antes de completar el cambio. Este proceso protege contra cambios no autorizados y mantiene la integridad de las comunicaciones. ### Contexto de Negocio El cambio de email es sensible porque: - El email es el identificador principal de login - Se usa para recuperacion de password - Se usa para notificaciones importantes - Debe ser verificado antes del cambio efectivo --- ## Criterios de Aceptacion - [x] **CA-001:** El usuario debe poder solicitar cambio de email - [x] **CA-002:** El sistema debe enviar verificacion al NUEVO email - [x] **CA-003:** El cambio no se aplica hasta verificar el nuevo email - [x] **CA-004:** El sistema debe validar que el nuevo email no exista en el tenant - [x] **CA-005:** El token de verificacion expira en 24 horas - [x] **CA-006:** El sistema debe notificar al email ANTERIOR sobre el cambio - [x] **CA-007:** El usuario debe confirmar con su password actual - [x] **CA-008:** Despues del cambio, todas las sesiones se invalidan ### Ejemplos de Verificacion ```gherkin Scenario: Solicitar cambio de email Given un usuario con email "viejo@empresa.com" When solicita cambiar a "nuevo@empresa.com" And confirma con su password actual Then el sistema valida que "nuevo@empresa.com" no existe And genera token de verificacion And envia email de verificacion a "nuevo@empresa.com" And el email actual permanece sin cambios And responde con status 200 Scenario: Verificar nuevo email Given una solicitud de cambio pendiente And un token de verificacion valido When el usuario hace clic en el link de verificacion Then el sistema actualiza el email del usuario And invalida todas las sesiones activas And envia notificacion al email anterior And redirige al login Scenario: Token de verificacion expirado Given un token de verificacion de mas de 24 horas When el usuario intenta usarlo Then el sistema responde con error And el mensaje es "Token expirado, solicita nuevo cambio" Scenario: Nuevo email ya existe Given un email "existente@empresa.com" ya registrado When un usuario intenta cambiar a ese email Then el sistema responde con status 409 And el mensaje es "Email no disponible" ``` --- ## Reglas de Negocio | ID | Regla | Validacion | |----|-------|------------| | RN-001 | Requiere password actual para solicitar cambio | Verificacion bcrypt | | RN-002 | Nuevo email debe ser unico en tenant | UNIQUE constraint | | RN-003 | Token de verificacion expira en 24 horas | expires_at check | | RN-004 | Solo una solicitud activa a la vez | Invalidar anteriores | | RN-005 | Notificar al email anterior | Email de seguridad | | RN-006 | Logout-all despues del cambio | Revocar tokens | | RN-007 | Nuevo email formato valido | Regex validation | ### Flujo de Cambio de Email ``` ┌─────────────────────────────────────────────────────────────────┐ │ 1. Usuario solicita cambio │ │ POST /api/v1/users/me/email/request-change │ │ Body: { newEmail, currentPassword } │ └───────────────────────────┬─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ 2. Validaciones │ │ - Password correcto │ │ - Nuevo email formato valido │ │ - Nuevo email no existe en tenant │ │ - No hay solicitud pendiente │ └───────────────────────────┬─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ 3. Crear solicitud │ │ - Guardar en email_change_requests │ │ - Generar token (32 bytes, hex) │ │ - expires_at = now + 24 hours │ └───────────────────────────┬─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ 4. Enviar email de verificacion │ │ To: nuevo@email.com │ │ Link: /verify-email-change?token={token} │ └───────────────────────────┬─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ 5. Usuario hace clic en link │ │ GET /api/v1/users/email/verify-change?token={token} │ └───────────────────────────┬─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ 6. Aplicar cambio │ │ - Actualizar users.email │ │ - Marcar solicitud como completada │ │ - Invalidar todas las sesiones │ │ - Enviar notificacion al email anterior │ └───────────────────────────┬─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ 7. Redirigir al login │ │ - Usuario debe iniciar sesion con nuevo email │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## Impacto en Capas ### Database | Elemento | Accion | Descripcion | |----------|--------|-------------| | Tabla | crear | `email_change_requests` | | Columna | - | `id` UUID PK | | Columna | - | `user_id` UUID FK | | Columna | - | `tenant_id` UUID FK | | Columna | - | `current_email` VARCHAR(255) | | Columna | - | `new_email` VARCHAR(255) | | Columna | - | `token_hash` VARCHAR(255) | | Columna | - | `expires_at` TIMESTAMPTZ | | Columna | - | `completed_at` TIMESTAMPTZ | | Columna | - | `created_at` TIMESTAMPTZ | | Indice | crear | `idx_email_change_user` | ### Backend | Elemento | Accion | Descripcion | |----------|--------|-------------| | Controller | agregar | `UsersController.requestEmailChange()` | | Controller | agregar | `UsersController.verifyEmailChange()` | | Method | crear | `UsersService.requestEmailChange()` | | Method | crear | `UsersService.verifyEmailChange()` | | DTO | crear | `RequestEmailChangeDto` | | Entity | crear | `EmailChangeRequest` | | Endpoint | crear | `POST /api/v1/users/me/email/request-change` | | Endpoint | crear | `GET /api/v1/users/email/verify-change` | ### Frontend | Elemento | Accion | Descripcion | |----------|--------|-------------| | Componente | crear | `ChangeEmailForm` | | Pagina | crear | `VerifyEmailChangePage` | | Modal | crear | `ConfirmPasswordModal` | --- ## Dependencias ### Depende de (Bloqueantes) | ID | Requerimiento | Estado | |----|---------------|--------| | RF-USER-001 | CRUD Usuarios | Tabla users | | RF-AUTH-004 | Logout | Para logout-all | ### Dependencias Externas | Servicio | Descripcion | |----------|-------------| | Email Service | Envio de verificacion | --- ## Especificaciones Tecnicas ### Endpoint POST /api/v1/users/me/email/request-change ```typescript // Request { "newEmail": "nuevo@empresa.com", "currentPassword": "MiPasswordActual123!" } // Response 200 { "message": "Se ha enviado un email de verificacion a nuevo@empresa.com", "expiresAt": "2025-12-06T10:30:00Z" } // Response 400 - Password incorrecto { "statusCode": 400, "message": "Password incorrecto" } // Response 409 - Email existe { "statusCode": 409, "message": "Email no disponible" } ``` ### Endpoint GET /api/v1/users/email/verify-change ```typescript // Request GET /api/v1/users/email/verify-change?token=abc123... // Response 200 (redirect) // Redirect to: /login?emailChanged=true // Response 400 - Token invalido { "statusCode": 400, "message": "Token invalido o expirado" } ``` ### Template de Email - Verificacion ```html Asunto: Verifica tu nuevo email - ERP Suite

Hola {{firstName}},

Recibimos una solicitud para cambiar tu email de:

{{currentEmail}} a {{newEmail}}

Haz clic en el siguiente enlace para confirmar el cambio:

Verificar nuevo email

Este enlace expira en 24 horas.

Si no solicitaste este cambio, ignora este email y considera cambiar tu password por seguridad.

``` ### Template de Email - Notificacion al Email Anterior ```html Asunto: Tu email ha sido cambiado - ERP Suite

Hola {{firstName}},

Te informamos que el email de tu cuenta ha sido cambiado.

Email anterior: {{oldEmail}}

Email nuevo: {{newEmail}}

Fecha: {{changeDate}}

Si no realizaste este cambio, contacta inmediatamente a soporte.

``` --- ## Datos de Prueba | Escenario | Entrada | Resultado | |-----------|---------|-----------| | Solicitud valida | newEmail, password correcto | 200, email enviado | | Password incorrecto | password erroneo | 400, "Password incorrecto" | | Email ya existe | email de otro usuario | 409, "Email no disponible" | | Email invalido | "notanemail" | 400, "Email invalido" | | Verificar token valido | token < 24h | 200, email cambiado | | Token expirado | token > 24h | 400, "Token expirado" | | Token ya usado | solicitud completada | 400, "Token ya utilizado" | --- ## Estimacion | Capa | Story Points | Notas | |------|--------------|-------| | Database | 1 | Tabla email_change_requests | | Backend | 3 | Endpoints, validaciones, emails | | Frontend | 2 | Form y pagina verificacion | | **Total** | **6** | | --- ## Notas Adicionales - Considerar periodo de gracia donde se puede revertir el cambio - Implementar notificacion push ademas de email - Rate limiting en solicitudes (max 3 por dia) - Log de todos los cambios de email para auditoria --- ## Historial de Cambios | Version | Fecha | Autor | Cambios | |---------|-------|-------|---------| | 1.0 | 2025-12-05 | System | Creacion inicial | --- ## Aprobaciones | Rol | Nombre | Fecha | Firma | |-----|--------|-------|-------| | Analista | System | 2025-12-05 | [x] | | Tech Lead | - | - | [ ] | | Product Owner | - | - | [ ] |