Saltar al contenido principal

Autenticación y Seguridad

Floutic implementa un sistema de autenticación robusto con múltiples capas de seguridad, siguiendo las mejores prácticas de la industria.

Sistema de Autenticación JWT

Tokens

Floutic utiliza un sistema dual de tokens:

  1. Access Token (JWT)

    • Almacenado en memoria (no en localStorage)
    • Vida corta (15 minutos por defecto)
    • Incluye información del usuario y session_id
    • Enviado en header Authorization: Bearer <token>
  2. Refresh Token

    • Almacenado en cookie HttpOnly
    • Vida larga (7 días por defecto)
    • Usado para renovar access tokens
    • No accesible desde JavaScript

Endpoints de Autenticación

Login Seguro

POST /api/auth/login_secure

Request:

{
"username": "usuario@ejemplo.com",
"password": "contraseña_segura"
}

Headers requeridos:

  • X-Session-ID: ID único de sesión generado por el cliente

Response:

{
"access_token": "eyJ...",
"csrf_token": "csrf_...",
"user": {
"id": 1,
"email": "usuario@ejemplo.com",
"username": "usuario",
"role": "empresa"
}
}

Cookies establecidas:

  • refresh_token: HttpOnly, SameSite=Strict
  • csrf_token: HttpOnly, SameSite=Strict

Refresh Token

POST /api/auth/refresh_secure

Headers requeridos:

  • X-Session-ID: Debe coincidir con el del token
  • Cookie: refresh_token

Response:

{
"access_token": "eyJ...",
"csrf_token": "csrf_..."
}

Verificar Sesión

GET /api/auth/verify_secure

Headers requeridos:

  • X-Session-ID: Debe coincidir con el del token
  • Cookie: refresh_token

Response:

{
"valid": true,
"authenticated": true,
"user": {
"id": 1,
"email": "usuario@ejemplo.com",
"username": "usuario",
"role": "empresa"
},
"access_token": "eyJ...",
"csrf_token": "csrf_..."
}

Logout

POST /api/auth/logout_secure

Headers requeridos:

  • X-Session-ID
  • Cookie: refresh_token
  • X-CSRF-Token: Token CSRF

Response:

{
"message": "Logout successful"
}

Protección CSRF

Floutic implementa protección CSRF robusta utilizando el patrón Double Submit Cookie:

  1. Token CSRF en Cookie

    • Generado en login/refresh
    • Almacenado en cookie csrf_token (HttpOnly, SameSite=Strict)
    • Incluido también en la respuesta JSON (para que el cliente lo lea)
  2. Validación en Servidor (verify_csrf_token)

    • Todos los requests que modifican estado (POST, PUT, DELETE, PATCH) en endpoints protegidos requieren validación.
    • El cliente debe enviar el token en el header X-CSRF-Token.
    • El servidor compara criptográficamente (secrets.compare_digest) el valor de la cookie csrf_token con el header X-CSRF-Token.
    • Si no coinciden o faltan, se rechaza con 403 Forbidden.
  3. Endpoints Protegidos

    • Actualización de perfil (PUT /me)
    • Cambio de contraseña
    • Cambio de email
    • Configuración de webhooks
    • Gestión de hitos y pagos
  4. Renovación

    • Token se renueva en cada refresh
    • Token se invalida en logout

Uso en Frontend

// El cliente Axios añade automáticamente el CSRF token
const response = await api.post('/api/projects', data);
// Header X-CSRF-Token se añade automáticamente

Session ID Único

Propósito

El session_id único por ventana previene la fuga de sesiones entre ventanas privadas.

Implementación

  1. Generación en Cliente

    • Cada ventana genera un session_id único
    • Almacenado en sessionStorage (aislado entre ventanas)
    • Formato: sess_<random>
  2. Inclusión en JWT

    • El session_id se incluye en el payload del JWT
    • Validado en cada request
  3. Validación en Servidor

    • login_secure: Lee X-Session-ID del header, lo incluye en JWT
    • refresh_secure: Valida que session_id del header coincida con el del token
    • verify_secure: Valida que session_id del header coincida con el del token

Flujo

┌─────────────────────────────────────┐
│ VENTANA 1 │
│ 1. Genera session_id: sess_abc123 │
│ 2. Login → JWT incluye sess_abc123 │
│ 3. Cookie creada con JWT │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ VENTANA 2 │
│ 1. Genera session_id: sess_xyz789 │
│ 2. Aunque tenga cookie compartida...│
│ 3. verify_secure compara: │
│ - Token: sess_abc123 │
│ - Header: sess_xyz789 │
│ - ❌ NO COINCIDEN → RECHAZADO │
└─────────────────────────────────────┘

Cookies Seguras

Configuración

Desarrollo:

domain=None              # Sin domain para máximo aislamiento
secure=False # HTTP permitido en localhost
samesite="strict" # Aislamiento máximo entre contextos
httponly=True # No accesible desde JavaScript

Producción:

domain="floutic.com"     # Para permitir subdominios
secure=True # Solo HTTPS
samesite="strict" # Aislamiento máximo
httponly=True # No accesible desde JavaScript

Cookies Utilizadas

  1. refresh_token

    • HttpOnly, SameSite=Strict
    • Vida: 7 días (configurable)
    • Usado para renovar access tokens
  2. csrf_token

    • HttpOnly, SameSite=Strict
    • Vida: 7 días (configurable)
    • Usado para protección CSRF

Sesiones Concurrentes

Límites

  • Máximo 10 sesiones concurrentes por usuario individual
  • Sin límite para usuarios de empresa (múltiples usuarios pueden estar logueados simultáneamente)

Configuración

MAX_CONCURRENT_SESSIONS = 10  # Configurable via env var

Gestión

  • Las sesiones se rastrean en Redis
  • Al exceder el límite, la sesión más antigua se invalida
  • El usuario recibe notificación de sesión expirada

Rate Limiting

Niveles

  1. Global: 60 requests/minuto por IP
  2. Login por Usuario: 5 intentos máximo
  3. Login por IP: 20 intentos máximo

Implementación

# Global rate limiting
@limiter.limit("60/minute")

# Login rate limiting
@limiter.limit("5/minute", key_func=lambda: f"login_user_{user_id}")
@limiter.limit("20/minute", key_func=lambda: f"login_ip_{ip}")

Respuestas

  • 429 Too Many Requests: Cuando se excede el límite
  • Retry-After: Header con tiempo de espera
  • Frontend muestra mensaje apropiado al usuario

Verificación de Email

Flujo de Registro

  1. Usuario se registra → Login automático
  2. Se envía email de verificación (token válido 48 horas)
  3. Usuario puede usar la plataforma normalmente pero ve banner recordatorio
  4. Al verificar email → Se envía email de bienvenida

Características

  • Sin restricciones: Los usuarios pueden usar la plataforma sin verificar email
  • Banner recordatorio: Aparece hasta verificar el email
  • Reenvío: Disponible con rate limit (1 cada 2 minutos)
  • Token seguro: Tokens hasheados y con expiración

Endpoints

  • POST /api/auth/verify-email - Verificar email con token
  • POST /api/auth/send-verification-email - Reenviar email de verificación

Más información en Verificación.

Validación de Contraseñas

Requisitos

  • Mínimo 8 caracteres
  • Al menos una mayúscula
  • Al menos una minúscula
  • Al menos un número
  • Al menos un carácter especial

Hash

  • bcrypt con salt rounds = 12
  • Nunca se almacenan contraseñas en texto plano

RBAC (Role-Based Access Control)

Sistema Multi-Rol

Floutic implementa un sistema de roles múltiples que permite a los usuarios tener varios roles simultáneamente:

  • Un usuario puede ser empresa Y experto al mismo tiempo
  • Cada rol tiene su propio perfil independiente con verificación separada
  • El usuario puede cambiar entre roles en cualquier momento
  • El rol activo (active_role) determina qué dashboard y funcionalidades están disponibles

Roles Disponibles

  1. admin: Acceso completo al sistema (solo puede ser asignado manualmente)
  2. empresa: Gestión de proyectos y pagos
  3. experto: Aplicación a proyectos y gestión de hitos

Estructura de Roles

{
"roles": ["empresa", "experto"], // Array de roles del usuario
"active_role": "empresa" // Rol actualmente activo
}

Gestión de Roles

Registro:

  • Al registrarse, el usuario puede seleccionar uno o ambos roles (empresa y/o experto)
  • Se crea un perfil inicial para el primer rol seleccionado
  • El active_role se establece al primer rol de la lista

Añadir Rol:

  • Endpoint: POST /api/auth/add-role
  • Permite añadir un nuevo rol al usuario autenticado
  • Requiere crear el perfil correspondiente después de añadir el rol

Cambiar Rol Activo:

  • Endpoint: POST /api/auth/switch-role
  • Cambia el active_role del usuario
  • Redirige automáticamente al dashboard del nuevo rol

Implementación

# Verificar si el usuario tiene un rol específico
if "admin" in user.roles:
# Usuario es admin
pass

# Verificar rol activo
if user.active_role == "empresa":
# Usuario está usando el rol de empresa
pass

# Dependencias de FastAPI
@router.get("/admin/users")
async def get_users(current_user: User = Depends(get_current_admin_user)):
# get_current_admin_user verifica que "admin" esté en user.roles
pass

@router.get("/projects/me")
async def get_my_projects(current_user: User = Depends(get_current_company_user)):
# get_current_company_user verifica que active_role == "empresa"
pass

Perfiles Multi-Rol

  • Cada rol tiene su propio perfil independiente
  • Un usuario con roles ["empresa", "experto"] puede tener:
    • Un perfil de empresa (con verificación independiente)
    • Un perfil de experto (con verificación independiente)
  • El endpoint /api/profiles/me devuelve el perfil del active_role actual
  • Cada perfil puede tener diferentes estados de verificación

Más Información