Saltar al contenido principal

Autenticación Frontend

Floutic implementa un sistema de autenticación seguro con múltiples capas de protección, utilizando HttpOnly cookies, CSRF tokens y session IDs únicos.

Sistema de Autenticación Segura

Características Principales

  • HttpOnly cookies: Tokens almacenados de forma segura
  • CSRF protection: Tokens CSRF en cookies HttpOnly
  • Session ID único: Por ventana para aislar sesiones
  • Refresh automático: Renovación transparente de tokens
  • Logout por inactividad: Configurable por rol
  • Validación de session_id: En todas las peticiones

Implementación

Servicio de Autenticación (src/lib/auth/secureAuth.ts)

Métodos Principales

class SecureAuthService {
// Login con credenciales
async login(credentials: { username: string; password: string }): Promise<void>

// Logout seguro
async logout(): Promise<void>

// Verificar sesión actual
async verifySession(): Promise<boolean>

// Refresh automático de tokens
async refreshToken(): Promise<void>

// Obtener usuario actual
getCurrentUser(): User | null
}

Session ID Único

Cada ventana genera un session_id único:

  • Almacenado en sessionStorage (aislado entre ventanas privadas)
  • Enviado en header X-Session-ID en cada petición
  • Incluido en el payload del JWT al crear tokens
  • Validado en el servidor para prevenir fuga de sesiones

Hook de Autenticación (useSecureAuth)

Hook principal para usar autenticación en componentes:

const {
user,
isAuthenticated,
isLoading,
login,
logout,
verifySession
} = useSecureAuth();

Características:

  • Estado reactivo de autenticación
  • Inicialización automática al montar
  • Refresh automático de tokens
  • Manejo de errores

Cliente API

El cliente Axios (src/lib/api/client.ts) incluye interceptores:

Request Interceptor

  • Añade X-Session-ID header
  • Añade X-CSRF-Token header
  • Añade Authorization header con access token

Response Interceptor

  • Maneja errores 401 (no autorizado)
  • Refresh automático de tokens
  • Retry logic para requests fallidos

Flujo de Autenticación

Login

  1. Usuario ingresa credenciales
  2. Frontend envía POST /api/auth/login_secure con:
    • username y password
    • Header X-Session-ID (generado en cliente)
  3. Backend valida credenciales y crea:
    • refresh_token en cookie HttpOnly
    • csrf_token en cookie HttpOnly
    • access_token en respuesta JSON
    • JWT incluye session_id del header
  4. Frontend almacena:
    • access_token en memoria (no localStorage)
    • session_id en sessionStorage
    • Estado de autenticación en Zustand store

Verificación de Sesión

  1. Al cargar la app, se llama a verify_secure
  2. Backend valida:
    • refresh_token en cookies
    • session_id del header coincide con el del token
  3. Si es válido, devuelve usuario y tokens
  4. Frontend actualiza estado de autenticación

Refresh de Tokens

  1. Cuando el access_token expira (o está próximo a expirar)
  2. Frontend llama a POST /api/auth/refresh_secure
  3. Backend valida:
    • refresh_token en cookies
    • session_id del header
  4. Genera nuevos tokens y los devuelve
  5. Frontend actualiza access_token en memoria

Logout

  1. Usuario hace logout
  2. Frontend llama a POST /api/auth/logout_secure
  3. Backend:
    • Invalida refresh_token en Redis blacklist
    • Elimina cookies
  4. Frontend:
    • Limpia estado de autenticación
    • Limpia session_id de sessionStorage
    • Redirige a login

Protección de Rutas

Middleware de Astro (src/middleware.ts)

El middleware verifica autenticación antes de servir páginas:

export async function onRequest(context, next) {
const { url, cookies } = context;

// Rutas públicas
if (isPublicRoute(url.pathname)) {
return next();
}

// Verificar autenticación
const authenticated = await verifyAuth(cookies);

if (!authenticated) {
return redirect('/login');
}

return next();
}

Protección de Componentes

Componentes protegidos verifican autenticación:

const Dashboard = () => {
const { isAuthenticated, isLoading } = useSecureAuth();

if (isLoading) return <Loading />;
if (!isAuthenticated) return <Redirect to="/login" />;

return <DashboardContent />;
};

Seguridad

HttpOnly Cookies

  • refresh_token: Token de renovación (HttpOnly, SameSite=Strict)
  • csrf_token: Token CSRF (HttpOnly, SameSite=Strict)

Configuración:

  • Desarrollo: domain=None, secure=False, SameSite=Strict
  • Producción: domain="floutic.com", secure=True, SameSite=Strict

CSRF Protection

  • Token CSRF en cookie HttpOnly
  • Token enviado en header X-CSRF-Token
  • Validación en servidor para requests mutantes (POST, PUT, DELETE, PATCH)

Session ID Único

  • Generado por ventana en sessionStorage
  • Enviado en header X-Session-ID
  • Validado en servidor para aislar sesiones entre ventanas privadas

Aislamiento de Sesiones

Triple protección:

  1. Cookies con SameSite=Strict (aislamiento a nivel de cookies)
  2. Sin domain en desarrollo (aislamiento a nivel de origen)
  3. Validación de session_id único (aislamiento a nivel de aplicación)

Logout por Inactividad

Configuración

const IDLE_TIMEOUTS = {
admin: 30 * 60 * 1000, // 30 minutos
empresa: 60 * 60 * 1000, // 1 hora
experto: 60 * 60 * 1000, // 1 hora
};

Hook useIdleTimeout

useIdleTimeout({
timeout: IDLE_TIMEOUTS[user.role],
onIdle: () => {
logout();
showWarning('Sesión expirada por inactividad');
}
});

Almacenamiento

sessionStorage

  • session_id: ID único por ventana
  • Aislado entre ventanas privadas

Memoria (no persistente)

  • access_token: Token de acceso actual
  • Se pierde al recargar la página

Cookies (HttpOnly)

  • refresh_token: Token de renovación
  • csrf_token: Token CSRF

Más Información