Skip to content
GitHub

Autenticación y Autorización

El sistema Nostromo utiliza JSON Web Tokens (JWT) para la autenticación stateless y Role-Based Access Control (RBAC) para la autorización granular.


A diferencia de sistemas tradicionales basados en sesión, Nostromo utiliza un enfoque Token-Based:

  1. Frontend envía credenciales (email, password) al endpoint /auth/login
  2. Backend valida contra hash en BD usando bcrypt
  3. Backend retorna un JWT firmado con JWT_SECRET
  4. Frontend almacena token en cookie sid (httpOnly)
  5. Requests subsiguientes incluyen token automáticamente

%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#2a2a2a', 'primaryTextColor': '#e0e0e0', 'lineColor': '#f38020'}}}%%
sequenceDiagram
    participant U as 👤 Usuario
    participant F as 🏝️ Sevastopol
    participant A as 🎯 Orchestrator
    participant D as 🗄️ PostgreSQL
    
    U->>F: Ingresa credenciales
    F->>A: POST /auth/login
    A->>D: SELECT * FROM auth.users WHERE email=$1
    D-->>A: User Hash
    A->>A: bcrypt.compare(pass, hash)
    
    alt ✅ Credenciales válidas
        A->>A: Generar JWT (sign)
        A-->>F: { token, user_data }
        Note over A,F: Set-Cookie: sid=<JWT>
        F->>U: Redirige al Dashboard
    else ❌ Inválidas
        A-->>F: 401 Unauthorized
        F->>U: Muestra error
    end

El JWT contiene información para el contexto de la solicitud, pero no contiene secretos:

JWT Payload
{
"userId": "uuid-v4-del-usuario",
"email": "[email protected]",
"role": "ADMIN",
"iat": 1703698000,
"exp": 1703784400
}
CampoDescripción
userIdUUID único del usuario
emailEmail para logging/audit
roleRol global (SUPER_ADMIN, ADMIN, USER, RO)
iatTimestamp de emisión
expExpiración (24h por defecto)

El backend utiliza un middleware authenticateToken que intercepta todas las rutas protegidas:

middleware/auth.ts
import jwt from 'jsonwebtoken';
import cookie from 'cookie';
export const authenticateToken = (req, res, next) => {
// 1. Intentar obtener token del header Authorization
const authHeader = req.headers['authorization'];
let token = authHeader?.split(' ')[1]; // Bearer TOKEN
// 2. Fallback: buscar en cookie sid
if (!token && req.headers.cookie) {
const parsed = cookie.parse(req.headers.cookie);
token = parsed.sid;
}
if (!token) {
return res.status(401).json({ success: false, error: 'No authorization token' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!);
req.user = decoded; // Inyecta usuario en el request
next();
} catch {
return res.status(403).json({ success: false, error: 'Invalid or expired token' });
}
};

Aunque el JWT valida la identidad, los permisos específicos se validan mediante el sistema RBAC:

RolAlcanceDescripción
SUPER_ADMIN🌍 GlobalAcceso total a todos los tenants y configuración de sistema
ADMIN🏢 TenantAcceso total dentro de su organización
USER🏢 TenantAcceso estándar para operar módulos
RO🏢 TenantSolo lectura para auditores
lib/rbac.ts
export const authorizeRoute = (req, res, next) => {
const { role } = req.user;
const route = req.originalUrl.split('?')[0];
if (!rbac.canAccess(role, route)) {
return res.status(403).json({
success: false,
error: `Access denied for role '${role}'`
});
}
next();
};