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 cors from "cors";
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import { AppDataSource } from "./config/data-source";
|
import { AppDataSource } from "./config/data-source";
|
||||||
|
import { requireAuth, AuthenticatedRequest } from "./middleware/auth.middleware";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
@@ -16,6 +17,12 @@ app.get("/api/health", (_req, res) => {
|
|||||||
res.json({ status: "ok", timestamp: new Date().toISOString() });
|
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()
|
AppDataSource.initialize()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log("Database connected");
|
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" });
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -29,6 +29,7 @@ services:
|
|||||||
DB_USER: ${DB_USER:-originsdigital}
|
DB_USER: ${DB_USER:-originsdigital}
|
||||||
DB_PASSWORD: ${DB_PASSWORD}
|
DB_PASSWORD: ${DB_PASSWORD}
|
||||||
JWT_SECRET: ${JWT_SECRET}
|
JWT_SECRET: ${JWT_SECRET}
|
||||||
|
SUPER_OAUTH_URL: ${SUPER_OAUTH_URL:-https://superoauth.tetardtek.com}
|
||||||
depends_on:
|
depends_on:
|
||||||
mysql:
|
mysql:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
Reference in New Issue
Block a user