Solucionar errores de CORS manteniendo seguridad de Authentik
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 46s

PROBLEMA:
- Frontend hacía fetch a APIs protegidas por Authentik
- Cuando sesión expiraba, Authentik redirigía causando error de CORS
- TypeError: Failed to fetch

SOLUCIÓN:
1. Backend inyecta window.__AUTHENTIK_USER__ en HTML inicial (app.js)
   - Servidor lee headers de Authentik y los pasa al frontend
   - Evita fetch innecesario en carga inicial

2. Frontend usa window.__AUTHENTIK_USER__ como fuente principal (useAuthentik.js)
   - Solo hace fetch cuando se fuerza refresh
   - Detecta errores de CORS como señal de sesión expirada
   - Muestra mensaje claro al usuario

3. App.vue detecta errores de autenticación en APIs
   - Cuando fetch falla con CORS, recarga página automáticamente
   - Authentik manejará la re-autenticación

SEGURIDAD:
- Todos los endpoints /api/* siguen protegidos por Authentik
- No se exponen APIs sin autenticación
- Headers de Authentik solo presentes con sesión válida
This commit is contained in:
2025-10-27 15:15:44 -06:00
parent ab0f79e103
commit 1ea50f0aa5
3 changed files with 136 additions and 11 deletions

View File

@@ -2,6 +2,7 @@ import express from 'express';
import morgan from 'morgan';
import path from 'path';
import { fileURLToPath } from 'url';
import { readFileSync } from 'fs';
import apiRouter from './routes/api.js';
import radiusRouter from './routes/radius.js';
import authRouter from './routes/auth.js';
@@ -27,8 +28,44 @@ export function createApp() {
// Simple health endpoint for reverse proxies / checks
app.get('/healthz', (_req, res) => res.json({ ok: true }));
app.get('/', (_req, res) => {
res.sendFile(path.join(__dirname, '..', 'public', 'index.html'));
// Servir index.html con información de usuario inyectada
app.get('/', (req, res) => {
const indexPath = path.join(__dirname, '..', 'public', 'index.html');
let html = readFileSync(indexPath, 'utf-8');
// Leer headers de Authentik
const username = req.headers['x-authentik-username'];
const email = req.headers['x-authentik-email'];
const name = req.headers['x-authentik-name'];
const groups = req.headers['x-authentik-groups'];
const uid = req.headers['x-authentik-uid'];
// Crear objeto de usuario
const userData = username ? {
authenticated: true,
user: {
username,
email,
name,
groups: groups ? groups.split('|').filter(g => g.trim()) : [],
uid
}
} : {
authenticated: false,
user: null
};
// Inyectar en el HTML antes de </head>
const injection = `
<script>
window.__AUTHENTIK_USER__ = ${JSON.stringify(userData)};
</script>
</head>`;
html = html.replace('</head>', injection);
res.setHeader('Content-Type', 'text/html');
res.send(html);
});
return app;