77 lines
2.2 KiB
TypeScript
77 lines
2.2 KiB
TypeScript
import { ConfidentialClientApplication } from "@azure/msal-node";
|
|
|
|
// ─── Azure AD Authentication ──────────────────────────────────────────────────
|
|
|
|
let msalClient: ConfidentialClientApplication | null = null;
|
|
|
|
/**
|
|
* Vérifie que les 3 variables d'environnement Azure AD sont présentes
|
|
*/
|
|
export function isAzureAdConfigured(): boolean {
|
|
return !!(
|
|
process.env.AZURE_AD_TENANT_ID &&
|
|
process.env.AZURE_AD_CLIENT_ID &&
|
|
process.env.AZURE_AD_CLIENT_SECRET
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Instancie le client MSAL (lazy, singleton)
|
|
*/
|
|
function getMsalClient(): ConfidentialClientApplication {
|
|
if (!isAzureAdConfigured()) {
|
|
throw new Error("Azure AD is not configured");
|
|
}
|
|
if (!msalClient) {
|
|
msalClient = new ConfidentialClientApplication({
|
|
auth: {
|
|
clientId: process.env.AZURE_AD_CLIENT_ID!,
|
|
authority: `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID}`,
|
|
clientSecret: process.env.AZURE_AD_CLIENT_SECRET!,
|
|
},
|
|
});
|
|
}
|
|
return msalClient;
|
|
}
|
|
|
|
/**
|
|
* Retourne l'URL de redirection Azure AD pour l'utilisateur
|
|
*/
|
|
export async function getAzureAuthUrl(): Promise<string> {
|
|
const client = getMsalClient();
|
|
const redirectUri =
|
|
process.env.AZURE_AD_REDIRECT_URI ||
|
|
"http://localhost:3000/api/auth/azure/callback";
|
|
return client.getAuthCodeUrl({
|
|
scopes: ["user.read"],
|
|
redirectUri,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Échange le code OAuth contre un token et retourne les infos utilisateur.
|
|
* azureAdId = homeAccountId = "{objectId}.{tenantId}" (~73 caractères)
|
|
*/
|
|
export async function handleAzureCallback(code: string) {
|
|
const client = getMsalClient();
|
|
const redirectUri =
|
|
process.env.AZURE_AD_REDIRECT_URI ||
|
|
"http://localhost:3000/api/auth/azure/callback";
|
|
|
|
const response = await client.acquireTokenByCode({
|
|
code,
|
|
scopes: ["user.read"],
|
|
redirectUri,
|
|
});
|
|
|
|
if (!response || !response.account) {
|
|
throw new Error("Failed to acquire token from Azure AD");
|
|
}
|
|
|
|
return {
|
|
azureAdId: response.account.homeAccountId, // "{objectId}.{tenantId}"
|
|
email: response.account.username, // UPN (ex: user@domain.com)
|
|
name: response.account.name || response.account.username,
|
|
};
|
|
}
|