feat: SuperOAuth token introspection middleware + /api/profile route
This commit is contained in:
@@ -3,6 +3,7 @@ import express from "express";
|
||||
import cors from "cors";
|
||||
import dotenv from "dotenv";
|
||||
import { AppDataSource } from "./config/data-source";
|
||||
import { requireAuth, AuthenticatedRequest } from "./middleware/auth.middleware";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
@@ -16,6 +17,12 @@ app.get("/api/health", (_req, res) => {
|
||||
res.json({ status: "ok", timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
// Route protégée — valide l'intégration SuperOAuth end-to-end
|
||||
app.get("/api/profile", requireAuth, (req, res) => {
|
||||
const { user } = req as AuthenticatedRequest;
|
||||
res.json({ success: true, data: { user } });
|
||||
});
|
||||
|
||||
AppDataSource.initialize()
|
||||
.then(() => {
|
||||
console.log("Database connected");
|
||||
|
||||
69
backend/src/middleware/auth.middleware.ts
Normal file
69
backend/src/middleware/auth.middleware.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
|
||||
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];
|
||||
|
||||
if (!token) {
|
||||
res.status(401).json({ success: false, error: "UNAUTHORIZED", message: "Access token required" });
|
||||
return;
|
||||
}
|
||||
|
||||
const superOAuthUrl = process.env.SUPER_OAUTH_URL;
|
||||
if (!superOAuthUrl) {
|
||||
console.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/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;
|
||||
}
|
||||
|
||||
(req as AuthenticatedRequest).user = data.data.user;
|
||||
next();
|
||||
} catch {
|
||||
res.status(500).json({ success: false, error: "AUTH_SERVICE_UNAVAILABLE", message: "Authentication service unreachable" });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user