Flow: Authentication
Acceso Seguro a la Plataforma
JTBD: “Cuando quiero acceder a mi salud, quiero entrar rápido y seguro”
Actor: Usuario (nuevo o existente)
Trigger: Abrir la app
Priority: P0 (Critical — gate to everything)
Duration: <30 segundos (returning user) | 2-3 minutos (new user)
Happy Path: Login Completo
Error Paths
Path 1: Email no registrado (sin código de invitación)
Path 2: Magic link expirado
Path 3: Cuenta bloqueada (brute force)
Path 4: Social login — email ya registrado con otro método
Screen 1: Login
Propósito: Punto de entrada único — email-first flow con magic link
+---------------------------------------------+
| ADEN Logo (centrado) |
| |
| "Medicina preventiva personalizada" |
| |
| [Login] [Registrarse] <- tabs |
| |
| Correo electrónico |
| +------------------------------------------+
| | maria@ejemplo.com |
| +------------------------------------------+
| Te enviaremos un enlace para ingresar |
| |
| [ CONTINUAR ] <- #0f2fc7 |
| |
| -------- o continua con -------- |
| |
| [G] Continuar con Google |
| [A] Continuar con Apple |
| |
| No tienes cuenta? Registrate |
+---------------------------------------------+
Happy path:
- Usuario ingresa email
- Email encontrado en DB → magic link enviado → redirect a verify-email
- Email no encontrado → muestra pantalla de código de invitación
Validaciones:
- Email: RFC 5322, 3-254 chars, required
- Error inline: “Email inválido” (formato incorrecto)
Copy:
- Título: “Medicina preventiva personalizada”
- CTA: “Continuar”
- Helper: “Te enviaremos un enlace para ingresar”
- Social: “o continua con”
- Footer: “No tienes cuenta? Registrate”
Transitions:
- Email existe →
/auth/verify-email(magic link sent) - Email no existe → Invitation code form (same page, step 2)
- Social login → OAuth flow → Dashboard (existing) or Register (new)
- Tab “Registrarse” →
/auth/register
Step 2: Código de Invitación (inline, misma página)
+---------------------------------------------+
| [icono llave, bg #e8ebf8] |
| |
| "No encontramos una cuenta con ese email." |
| "Si tienes un código de invitación, |
| ingresalo aquí." |
| |
| Código de invitación |
| +------------------------------------------+
| | ADEN-XXXX-XXXX |
| +------------------------------------------+
| |
| [ VOLVER ] [ VERIFICAR ] |
+---------------------------------------------+
Validaciones:
- Código: formato
ADEN-XXXX-XXXX, required - Error: “Código inválido” | “Código ya utilizado” | “Código expirado”
Transitions:
- Código válido →
/auth/register?email=...(pre-fill) - “Volver” → Step 1 (email form)
Screen 2: Registro
Propósito: Crear cuenta nueva con datos mínimos
+---------------------------------------------+
| ADEN Logo (centrado) |
| |
| "Medicina preventiva personalizada" |
| |
| [Login] [Registrarse] <- tabs |
| |
| Nombre completo |
| +------------------------------------------+
| | María Garcia |
| +------------------------------------------+
| |
| Correo electrónico |
| +------------------------------------------+
| | maria@ejemplo.com <- pre-filled |
| +------------------------------------------+
| |
| Código de invitación |
| +------------------------------------------+
| | ADEN-XXXX-XXXX |
| +------------------------------------------+
| Te enviaremos un enlace para verificar |
| tu cuenta |
| |
| [x] Acepto los Terminos y Condiciones |
| y la Politica de Privacidad |
| |
| [ CONTINUAR ] <- #0f2fc7 |
| |
| Ya tienes cuenta? Inicia sesión |
+---------------------------------------------+
Happy path:
- Campos pre-filled si viene de login (email) o invitation (código)
- Usuario completa nombre, acepta terminos
- Submit → cuenta creada → redirect a verify-email
Validaciones (from edge-cases.md):
| Campo | Regla | Error |
|---|---|---|
| Nombre | Required, 2-100 chars, letras/números/espacios/guiones | “Mínimo 2 caracteres” / “Caracteres no permitidos” |
| Required, RFC 5322, 3-254 chars, unique | “Email inválido” / “Email ya registrado” | |
| Código | Required, formato ADEN-XXXX-XXXX | “Código inválido” |
| Terminos | Required checkbox | “Debes aceptar los terminos” |
Error paths:
- Email ya registrado → “Email ya registrado” + links “Iniciar sesión” / “Olvidaste contraseña?” / “Usar otro email”
- Security: no revelar si el email existe vía timing; response time uniforme
Copy:
- Título: “Medicina preventiva personalizada”
- CTA: “Continuar”
- Helper: “Te enviaremos un enlace para verificar tu cuenta”
- Terminos: “Acepto los Terminos y Condiciones y la Politica de Privacidad”
- Footer: “Ya tienes cuenta? Inicia sesión”
Transitions:
- Éxito →
/auth/verify-email - “Terminos y Condiciones” →
/auth/terms(view-only) - “Politica de Privacidad” →
/auth/privacy(view-only) - “Inicia sesión” →
/auth
Screen 3: Verificar Email
Propósito: Confirmar que el usuario controla el email ingresado
+---------------------------------------------+
| [icono email, bg #e8ebf8, grande] |
| |
| "Verifica tu correo electrónico" |
| |
| Hemos enviado un enlace de |
| verificación a |
| maria@ejemplo.com |
| |
| Revisa tu bandeja de entrada y haz |
| clic en el enlace para continuar. |
| |
| -----------------------------------------|
| |
| No recibiste el correo? |
| |
| [ REENVIAR ENLACE ] <- outline |
| |
| Email incorrecto? Cambiar email |
| |
| <- Volver a iniciar sesión |
+---------------------------------------------+
Happy path:
- Post-registro: usuario ve esta pantalla
- Abre email → click magic link → token validado → redirect
- Si es nuevo usuario → Onboarding Step 1
- Si es usuario existente (magic link login) → Dashboard
Resend logic:
- Click “Reenviar enlace” → nuevo email enviado
- Visual feedback: botón cambia a “Enlace reenviado” (verde, 3s)
- Rate limit: max 3 reenvios por hora por email
- Cooldown visual: botón deshabilitado 60s después de cada reenvio
Error paths:
- Email no llega (user waits) → sugerencia “Revisa tu carpeta de spam”
- Email no llega después de 24h → ver CASE 2 en edge-cases:
- Day 1: Email de verificación (normal)
- Day 2: Reminder email + in-app banner
- Day 3: Block access → “Verifica tu email para continuar”
- Day 7: Auto-delete account (unverified)
Copy:
- Título: “Verifica tu correo electrónico”
- Body: “Hemos enviado un enlace de verificación a [email]”
- Helper: “Revisa tu bandeja de entrada y haz clic en el enlace para continuar.”
- Resend: “No recibiste el correo?” / “Reenviar enlace”
- Confirm: “Enlace reenviado”
- Fix: “Email incorrecto? Cambiar email”
Transitions:
- Magic link clicked → Dashboard (existing) o Onboarding (new)
- “Cambiar email” →
/auth/register - “Volver a iniciar sesión” →
/auth
Screen 4: Olvide Contraseña
Propósito: Permitir recuperación de acceso vía email
+---------------------------------------------+
| |
| "Recuperar contraseña" |
| |
| Ingresa tu correo electrónico y te |
| enviaremos un enlace para restablecer |
| tu contraseña. |
| |
| Correo electrónico |
| +----------------------------------------+
| | maria@ejemplo.com |
| +----------------------------------------+
| |
| [ ENVIAR ENLACE ] <- #0f2fc7 |
| |
| <- Volver a iniciar sesión |
+---------------------------------------------+
Estado éxito (reemplaza form):
+---------------------------------------------+
| [icono check, bg emerald-100] |
| |
| "Correo enviado" |
| |
| Revisa tu bandeja de entrada y sigue |
| las instrucciones para restablecer |
| tu contraseña. |
| |
| No lo ves? Revisa tu carpeta de spam. |
| |
| <- Volver a iniciar sesión |
+---------------------------------------------+
Happy path:
- Usuario ingresa email
- Submit → email enviado (siempre muestra éxito, incluso si email no existe — security)
- Email contiene link → click → pantalla reset password → nueva contraseña → login
Rate limit (from edge-cases.md):
- Max 3 solicitudes por hora por email
- Si excede: “Espera 1 hora antes de intentar de nuevo”
Validaciones:
- Email: RFC 5322, required
- Error: “Email inválido” (formato)
Security:
- Siempre mostrar estado de éxito (no revelar si email existe o no)
- Reset link expira en 1 hora
- Link es de un solo uso
Copy:
- Título: “Recuperar contraseña”
- Body: “Ingresa tu correo electrónico y te enviaremos un enlace para restablecer tu contraseña.”
- CTA: “Enviar enlace”
- Success título: “Correo enviado”
- Success body: “Revisa tu bandeja de entrada y sigue las instrucciones para restablecer tu contraseña.”
- Spam hint: “No lo ves? Revisa tu carpeta de spam.”
- Back: “Volver a iniciar sesión”
Transitions:
- Submit → Success state (misma página, form se oculta)
- “Volver a iniciar sesión” →
/auth
Screen 5: MFA Setup
Propósito: Seguridad adicional opcional (recomendada)
Trigger: Ofrecido durante onboarding (post-verificación) o desde Ajustes
+---------------------------------------------+
| |
| "Protege tu cuenta" |
| |
| Agrega una capa extra de seguridad. |
| Te pediremos un código adicional al |
| iniciar sesión. |
| |
| Elige tu método: |
| |
| +----------------------------------------+
| | [icono phone] |
| | SMS |
| | Recibe un código por mensaje de texto |
| +----------------------------------------+
| |
| +----------------------------------------+
| | [icono shield] |
| | App de autenticación |
| | Google Authenticator, Authy, etc. |
| +----------------------------------------+
| |
| [ CONFIGURAR ] <- #0f2fc7 |
| [ AHORA NO ] <- outline |
| |
| Puedes configurar esto después en |
| Ajustes > Seguridad |
+---------------------------------------------+
MFA: Flujo SMS
+---------------------------------------------+
| "Verificar tu teléfono" |
| |
| Número de teléfono |
| +----------------------------------------+
| | +57 | 300 123 4567 |
| +----------------------------------------+
| |
| [ ENVIAR CODIGO ] <- #0f2fc7 |
+---------------------------------------------+
| (SMS enviado)
v
+---------------------------------------------+
| "Ingresa el código" |
| |
| Enviamos un código a +57 ***4567 |
| |
| [ _ ] [ _ ] [ _ ] [ _ ] [ _ ] [ _ ] |
| |
| No recibiste el código? Reenviar (45s) |
| |
| [ VERIFICAR ] <- #0f2fc7 |
+---------------------------------------------+
MFA: Flujo App Autenticador
+---------------------------------------------+
| "Configura tu app de autenticación" |
| |
| 1. Abre tu app (Google Authenticator, |
| Authy, etc.) |
| 2. Escanea este código QR: |
| |
| +--------+ |
| | QR | |
| | CODE | |
| +--------+ |
| |
| No puedes escanear? Usa este código: |
| ABCD-EFGH-IJKL-MNOP |
| |
| 3. Ingresa el código de 6 digitos: |
| [ _ ] [ _ ] [ _ ] [ _ ] [ _ ] [ _ ] |
| |
| [ VERIFICAR ] <- #0f2fc7 |
+---------------------------------------------+
Happy path:
- Usuario elige método (SMS o app)
- Completa verificación
- Codigos de respaldo generados (10 codigos de un solo uso)
- Confirmación: “MFA activado”
Error paths:
- Código SMS incorrecto → “Código incorrecto. Intenta de nuevo.” (max 3 intentos)
- Código app incorrecto → “Código incorrecto. Asegurate de que el reloj de tu teléfono este sincronizado.”
- SMS no llega → “Reenviar” (cooldown 45s, max 3 reenvios)
- Teléfono no válido → “Número de teléfono inválido”
Codigos de respaldo:
+---------------------------------------------+
| "Guarda tus codigos de respaldo" |
| |
| Si pierdes acceso a tu método de MFA, |
| usa uno de estos codigos para entrar. |
| |
| +----------------------------------------+
| | a1b2-c3d4 e5f6-g7h8 |
| | i9j0-k1l2 m3n4-o5p6 |
| | q7r8-s9t0 u1v2-w3x4 |
| | y5z6-a7b8 c9d0-e1f2 |
| | g3h4-i5j6 k7l8-m9n0 |
| +----------------------------------------+
| |
| [COPIAR] [DESCARGAR] |
| |
| Cada código solo se puede usar una vez. |
| |
| [ LISTO ] <- #0f2fc7 |
+---------------------------------------------+
Transitions:
- “Ahora no” → continua onboarding / dashboard
- MFA completado → codigos de respaldo → onboarding / dashboard
- Desde Ajustes → Ajustes > Seguridad (return)
Screen 6: Terminos y Condiciones / Politica de Privacidad
Propósito: Documentos legales requeridos, view-only
Terminos y Condiciones
+---------------------------------------------+
| [<] Terminos y Condiciones |
| Actualizado: 15 Enero 2026 |
|---------------------------------------------|
| |
| 1. Aceptación de los Términos |
| Al acceder y utilizar la plataforma ADEN, |
| usted acepta estar sujeto a estos... |
| |
| 2. Descripción del Servicio |
| ADEN es una plataforma de medicina |
| preventiva personalizada que ofrece... |
| |
| 3. Datos de Salud |
| ... |
| |
| 4. Privacidad y Seguridad |
| ... |
| |
| 5. Responsabilidad |
| ... |
+---------------------------------------------+
Politica de Privacidad
+---------------------------------------------+
| [<] Politica de Privacidad |
| Actualizado: 15 Enero 2026 |
|---------------------------------------------|
| |
| 1. Información que Recopilamos |
| +----------+ +----------+ +----------+ |
| | Personal | | Salud | | Uso | |
| | Nombre, | | Labs, | | Páginas, | |
| | email... | | historial| | clicks.. | |
| +----------+ +----------+ +----------+ |
| |
| 2. Como Usamos tu Información |
| ... |
| |
| 3. Tus Derechos (Habeas Data) |
| ... |
+---------------------------------------------+
Behavior:
- View-only, scroll
- Back button → return to registration
- No user action required (acceptance is vía checkbox on registration)
- Last updated date visible in header
Transitions:
- Back button →
/auth/register
Biometric Authentication Flow (Returning Users)
ADEN uses device biometrics (FaceID / TouchID) for returning users who have previously authenticated.
Flow Diagram
Smart Login (Remember User)
+---------------------------------------------+
| |
| [Avatar María] |
| |
| "Hola, María" |
| |
| [FaceID icon] |
| Toca para ingresar |
| |
| No eres María? Usar otra cuenta |
+---------------------------------------------+
Rules:
- Biometric enabled after first successful login (user opt-in prompt)
- Biometric bypasses email entry but NOT session refresh
- If device biometric changes (e.g., new fingerprint added), invalidate stored credentials
- If app not opened for >30 days, require full re-authentication
- “Usar otra cuenta” → clears stored credentials, shows full login
Deep Link Handling
Email Verification Link
URL format: https://app.aden.health/auth/verify?token=abc123&type=email_verification
Flow:
1. User taps link in email client
2. App installed?
→ Yes: Universal link → app opens → token validated → redirect
→ No: Web fallback → browser → token validated → app store prompt
3. Token validation:
→ Valid: Account verified → redirect to next step
→ Expired (>24h): "Este enlace ya expiro. Reenviar enlace."
→ Invalid: "Enlace invalido. Volver al inicio."
→ Already used: "Tu cuenta ya está verificada. Iniciar sesión."
Password Reset Link
URL format: https://app.aden.health/auth/reset?token=xyz789&type=password_reset
Flow:
1. User taps link in email client
2. Token validation:
→ Valid: Show new password form
→ Expired (>1h): "Este enlace ya expiro. Solicitar nuevo enlace."
→ Invalid: "Enlace invalido."
→ Already used: "Este enlace ya fue utilizado."
Magic Link (Login)
URL format: https://app.aden.health/auth/login?token=def456&type=magic_link
Flow:
1. User taps link in email client
2. Token validation:
→ Valid (<15min): Session created → Dashboard
→ Expired (>15min): "Este enlace ya expiro. Solicitar nuevo enlace."
→ Invalid: "Enlace invalido."
Session Management
Token Architecture
Access Token:
- Type: JWT
- Expiry: 15 minutes
- Storage: Memory only (not localStorage)
- Refresh: Automatic vía refresh token
Refresh Token:
- Type: Opaque
- Expiry: 30 days
- Storage: Secure httpOnly cookie
- Rotation: New refresh token on each use (old one invalidated)
Biometric Token:
- Type: Device-bound credential
- Storage: Secure Enclave (iOS) / Keystore (Android)
- Expiry: 30 days (or until biometric change detected)
Session Lifecycle
Logout
Explicit logout:
1. Clear access token (memory)
2. Revoke refresh token (server-side)
3. Clear biometric credential
4. Redirect to login
Implicit logout (session expired):
1. Show: "Tu sesión expiró"
2. Options: [Iniciar sesión]
3. Preserve current URL for post-login redirect
Security Considerations
Brute Force Protection
Login attempts (per email):
- 1-3 attempts: Normal
- 4th attempt: CAPTCHA required
- 5th attempt: Account locked 15 minutes
- 10th attempt: Account locked 1 hour + email notification
- 20th attempt: Account locked indefinitely + security team notified
IP-based throttling:
- 100 login attempts per IP per hour (across all accounts)
- Exceeded: IP blocked 1 hour + logged
Account Lockout
Trigger: 5 failed login attempts
Behavior:
1. Lock account for 15 minutes
2. Show: "Tu cuenta está bloqueada temporalmente por seguridad. Intenta de nuevo en 15 minutos."
3. Send email: "Detectamos varios intentos de inicio de sesión fallidos. Si no fuiste tu, te recomendamos cambiar tu contraseña."
4. CTA in email: [Cambiar contraseña] [Fui yo, ignorar]
Auto-unlock: After 15 minutes
Manual unlock: Vía password reset link
Rate Limits (Auth-specific)
| Action | Limit | Window | Error Message |
|---|---|---|---|
| Login attempts | 5 per email | 15 min | “Cuenta bloqueada temporalmente” |
| Magic link requests | 3 per email | 1 hour | “Espera un momento antes de solicitar otro enlace” |
| Password reset | 3 per email | 1 hour | “Espera 1 hora antes de intentar de nuevo” |
| Email verification resend | 3 per email | 1 hour | “Enlace reenviado. Revisa tu bandeja.” |
| MFA code attempts | 3 per session | Per attempt | “Código incorrecto. Intenta de nuevo.” |
| Registration (per IP) | 5 accounts | 24 hours | “Demasiados registros desde esta ubicación” |
Additional Security Measures
- HTTPS only (HSTS enforced)
- CSRF tokens on all forms
- Content Security Policy headers
- Input sanitization (XSS prevention)
- SQL injection prevention (parameterized queries)
- Timing-attack prevention on email lookups (constant-time response)
- Magic links: single-use, short-lived (15min)
- Password reset links: single-use, 1 hour expiry
- Session invalidation on password change (all devices)
- Device fingerprinting for suspicious login detection
- Geo-anomaly detection (login from new country → email alert)
Critical Metrics
| Métrica | Meta | Impacto |
|---|---|---|
| Login → Dashboard | <5s (returning) | Friction kills retention |
| Registration completion | >85% | Funnel health |
| Email verification rate | >90% (Day 1) | Unverified = lost user |
| Magic link click-through | >80% | Email deliverability |
| Biometric adoption | >60% (prompted users) | Reduces friction |
| MFA adoption | >30% (optional) | Security posture |
| Account lockout rate | <1% | Indicates UX or attack issues |
| Password reset usage | <5% monthly | High = password UX problem |
Implementation Notes
- Validation: En vivo, per-field (no esperar al final)
- Skeleton loading: 260-500ms en primera carga
- FadeInUp animations: 260ms per element, staggered 0.2s
- Copy: Español colombiano, sin jerga técnica
- Error recovery: Siempre opción de “atrás” o contactar soporte
- Magic link: Método principal de autenticación (passwordless)
- Invitation codes: Required para registro (beta cerrada)
- Social login: Google y Apple como alternativas
- Biometric: Ofrecido post-primer-login, opt-in
- MFA: Opcional, recomendado, configurable en Ajustes > Seguridad
- Deep links: Universal links (iOS) / App Links (Android) con web fallback