All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 54s
79 lines
2.4 KiB
TypeScript
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" });
|
|
}
|
|
};
|