Implementar autenticación Authentik completa
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 25s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 25s
- Backend: Nuevos endpoints /api/auth/status y /api/auth/check-group - Frontend: Composable useAuthentik para gestión de autenticación - Frontend: Componentes UserDropdown, UserAvatar, SessionStatusButton, GroupCheckButton - Frontend: Integración en topbar con dropdown de usuario - Config: URLs de Authentik y configuración de avatares - Lectura de headers x-authentik-* inyectados por Traefik - Verificación de grupos RBAC (frontend y backend) - Validación de sesión contra Authentik
This commit is contained in:
@@ -4,6 +4,7 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import apiRouter from './routes/api.js';
|
||||
import radiusRouter from './routes/radius.js';
|
||||
import authRouter from './routes/auth.js';
|
||||
|
||||
export function createApp() {
|
||||
const app = express();
|
||||
@@ -20,6 +21,9 @@ export function createApp() {
|
||||
// REST API
|
||||
app.use('/api', apiRouter);
|
||||
|
||||
// Auth API
|
||||
app.use('/api/auth', authRouter);
|
||||
|
||||
// Simple health endpoint for reverse proxies / checks
|
||||
app.get('/healthz', (_req, res) => res.json({ ok: true }));
|
||||
|
||||
|
||||
90
node-api/src/routes/auth.js
Normal file
90
node-api/src/routes/auth.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Router } from 'express';
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* Endpoint para verificar el estado de autenticación en tiempo real
|
||||
* Consulta los headers inyectados por Authentik Proxy Outpost
|
||||
*/
|
||||
router.get('/status', (req, res) => {
|
||||
// Establecer headers para prevenir caching
|
||||
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
||||
res.setHeader('Pragma', 'no-cache');
|
||||
res.setHeader('Expires', '0');
|
||||
|
||||
// Leer headers de Authentik en tiempo real
|
||||
const headers = req.headers;
|
||||
|
||||
const username = headers['x-authentik-username'];
|
||||
const email = headers['x-authentik-email'];
|
||||
const name = headers['x-authentik-name'];
|
||||
const groups = headers['x-authentik-groups'];
|
||||
const uid = headers['x-authentik-uid'];
|
||||
const appSlug = headers['x-authentik-meta-app'];
|
||||
const outpostName = headers['x-authentik-meta-outpost'];
|
||||
|
||||
// Si no hay username, no hay sesión activa en Authentik
|
||||
if (!username) {
|
||||
return res.json({
|
||||
authenticated: false,
|
||||
user: null,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
// Sesión activa
|
||||
res.json({
|
||||
authenticated: true,
|
||||
user: {
|
||||
username,
|
||||
email,
|
||||
name,
|
||||
groups: groups ? groups.split('|').filter(g => g.trim()) : [],
|
||||
uid,
|
||||
appSlug,
|
||||
outpostName
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Endpoint para verificar membresía de grupo desde el backend
|
||||
* Valida contra los headers de Authentik en el servidor
|
||||
*/
|
||||
router.post('/check-group', (req, res) => {
|
||||
const { groupName } = req.body || {};
|
||||
|
||||
if (!groupName || typeof groupName !== 'string') {
|
||||
return res.status(400).json({
|
||||
ok: false,
|
||||
error: 'Group name is required'
|
||||
});
|
||||
}
|
||||
|
||||
// Leer headers de Authentik
|
||||
const headers = req.headers;
|
||||
const authentikGroups = headers['x-authentik-groups'];
|
||||
|
||||
// Si no hay header de grupos, el usuario no está autenticado o no tiene grupos
|
||||
if (!authentikGroups) {
|
||||
return res.json({
|
||||
hasGroup: false,
|
||||
groups: []
|
||||
});
|
||||
}
|
||||
|
||||
// Parsear los grupos (separados por |)
|
||||
const userGroups = authentikGroups.split('|').filter(g => g.trim());
|
||||
|
||||
// Verificar si el usuario tiene el grupo solicitado
|
||||
const hasGroup = userGroups.includes(groupName);
|
||||
|
||||
res.json({
|
||||
hasGroup,
|
||||
groups: userGroups,
|
||||
checkedGroup: groupName
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user