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

Implementación

Floutic implementa protección CSRF con tokens HttpOnly:

  1. Token CSRF en Cookie

    • Generado en login/refresh
    • Almacenado en cookie HttpOnly
    • Incluido en respuesta JSON
  2. Validación en Servidor

    • Todos los requests mutantes (POST, PUT, DELETE, PATCH) requieren CSRF token
    • Token debe estar en header X-CSRF-Token
    • Token debe coincidir con el de la cookie
  3. 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

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)

Roles

  1. admin: Acceso completo al sistema
  2. empresa: Gestión de proyectos y pagos
  3. experto: Aplicación a proyectos y gestión de hitos

Implementación

@router.get("/admin/users")
@require_role("admin")
async def get_users():
# Solo admins pueden acceder
pass

Más Información