Files
originsdigital/backend/src/middleware/auth.middleware.ts
Tetardtek 379a9a115b
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 54s
fix(security): isActive defense-in-depth, MIME magic bytes upload, tenantId=origins OAuth
2026-03-15 17:34:19 +01:00

79 lines
2.4 KiB
TypeScript

import { Request, Response, NextFunction } from "express";
import logger from "../utils/logger";
export interface AuthenticatedUser {
id: string;
email: string | null;
nickname: string;
isActive: boolean;
linkedProviders: string[];
}
export interface AuthenticatedRequest extends Request {
user: AuthenticatedUser;
}
/**
* Middleware d'authentification — Token Introspection via SuperOAuth
*
* Valide le Bearer token auprès de SuperOAuth (POST /api/auth/token/validate).
* Aucun secret JWT partagé — SuperOAuth garde le contrôle total.
*
* Flow :
* 1. Extraire le Bearer token du header Authorization
* 2. Appeler SuperOAuth /api/auth/token/validate
* 3. Si valid → attacher req.user et continuer
* 4. Sinon → 401
*/
export const requireAuth = async (
req: Request,
res: Response,
next: NextFunction
): Promise<void> => {
const token =
req.headers.authorization?.split(" ")[1] ??
(req.cookies as Record<string, string>)?.od_token;
if (!token) {
res.status(401).json({ success: false, error: "UNAUTHORIZED", message: "Access token required" });
return;
}
const superOAuthUrl = process.env.SUPER_OAUTH_URL;
if (!superOAuthUrl) {
logger.error("SUPER_OAUTH_URL not configured");
res.status(500).json({ success: false, error: "INTERNAL_ERROR", message: "Auth service not configured" });
return;
}
try {
const response = await fetch(`${superOAuthUrl}/api/v1/auth/token/validate`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
const data = await response.json() as {
success: boolean;
data?: { valid: boolean; user?: AuthenticatedUser };
error?: string;
};
if (!response.ok || !data.data?.valid || !data.data.user) {
res.status(401).json({ success: false, error: data.error ?? "INVALID_TOKEN", message: "Invalid or expired token" });
return;
}
if (!data.data.user.isActive) {
res.status(401).json({ success: false, error: "ACCOUNT_DISABLED", message: "Account is disabled" });
return;
}
(req as AuthenticatedRequest).user = data.data.user;
next();
} catch (err) {
logger.error("requireAuth — auth service unreachable", { err });
res.status(500).json({ success: false, error: "AUTH_SERVICE_UNAVAILABLE", message: "Authentication service unreachable" });
}
};