300 lines
11 KiB
JavaScript
300 lines
11 KiB
JavaScript
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||
import * as msal from '@azure/msal-browser';
|
||
// ✅ Correction: Import de API_BASE_URL
|
||
import { msalConfig, loginRequest, API_BASE_URL } from '../authConfig';
|
||
|
||
const AuthContext = createContext();
|
||
|
||
export const useAuth = () => {
|
||
const context = useContext(AuthContext);
|
||
if (!context) throw new Error('useAuth must be used within an AuthProvider');
|
||
return context;
|
||
};
|
||
|
||
const msalInstance = new msal.PublicClientApplication(msalConfig);
|
||
|
||
export const AuthProvider = ({ children }) => {
|
||
const [user, setUser] = useState(null);
|
||
const [userGroups, setUserGroups] = useState([]);
|
||
const [isAuthorized, setIsAuthorized] = useState(false);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [isMsalInitialized, setIsMsalInitialized] = useState(false);
|
||
|
||
// ✅ Fonction corrigée pour construire l'URL
|
||
const getApiUrl = (endpoint) => {
|
||
const cleanEndpoint = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint;
|
||
// API_BASE_URL est "/api", donc cela retourne "/api/endpoint"
|
||
return `${API_BASE_URL}/${cleanEndpoint}`;
|
||
};
|
||
|
||
// --- Vérifie l'autorisation de l'utilisateur via groupes
|
||
const checkUserAuthorization = async (userPrincipalName, accessToken) => {
|
||
try {
|
||
const response = await fetch(getApiUrl('check-user-groups'), {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${accessToken}`
|
||
},
|
||
body: JSON.stringify({ userPrincipalName })
|
||
});
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
setUserGroups(data.groups || []);
|
||
setIsAuthorized(data.authorized || false);
|
||
return data;
|
||
}
|
||
return { authorized: false, groups: [] };
|
||
} catch (error) {
|
||
console.error('Erreur vérification groupes:', error);
|
||
return { authorized: false, groups: [] };
|
||
}
|
||
};
|
||
|
||
// --- Synchronisation utilisateur connecté
|
||
const syncUserToDatabase = async (entraUser, accessToken) => {
|
||
try {
|
||
const response = await fetch(getApiUrl('initial-sync'), {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${accessToken}`
|
||
},
|
||
body: JSON.stringify(entraUser)
|
||
});
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
console.log('Utilisateur synchronisé:', entraUser.userPrincipalName);
|
||
return data;
|
||
}
|
||
} catch (error) {
|
||
console.error('Erreur synchronisation utilisateur:', error);
|
||
}
|
||
return null;
|
||
};
|
||
|
||
// --- Full sync admin
|
||
const fullSyncDatabase = async (accessToken) => {
|
||
try {
|
||
const response = await fetch(getApiUrl('initial-sync'), {
|
||
method: 'POST',
|
||
headers: { 'Authorization': `Bearer ${accessToken}` }
|
||
});
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
console.log('Full sync terminée:', data);
|
||
return data;
|
||
}
|
||
} catch (error) {
|
||
console.error('Erreur full sync:', error);
|
||
}
|
||
return null;
|
||
};
|
||
|
||
// --- S'assurer que MSAL est initialisé avant tout appel
|
||
const ensureMsalInitialized = async () => {
|
||
if (!isMsalInitialized) {
|
||
try {
|
||
await msalInstance.initialize();
|
||
setIsMsalInitialized(true);
|
||
console.log('MSAL initialisé');
|
||
} catch (error) {
|
||
console.error('Erreur initialisation MSAL:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
};
|
||
|
||
// --- Initialisation au chargement
|
||
useEffect(() => {
|
||
const initializeMsal = async () => {
|
||
try {
|
||
await ensureMsalInitialized();
|
||
|
||
const accounts = msalInstance.getAllAccounts();
|
||
if (accounts.length > 0) {
|
||
try {
|
||
const response = await msalInstance.acquireTokenSilent({
|
||
...loginRequest,
|
||
account: accounts[0]
|
||
});
|
||
await handleSuccessfulAuth(response);
|
||
} catch (error) {
|
||
console.log('Token silent acquisition failed:', error);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error("Erreur d'initialisation MSAL:", error);
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
initializeMsal();
|
||
}, []);
|
||
|
||
// --- Gestion login réussi
|
||
const handleSuccessfulAuth = async (authResponse) => {
|
||
try {
|
||
const account = authResponse.account;
|
||
const accessToken = authResponse.accessToken;
|
||
|
||
let entraUser = {
|
||
id: account.homeAccountId,
|
||
displayName: account.name,
|
||
userPrincipalName: account.username,
|
||
mail: account.username
|
||
};
|
||
|
||
const graphResponse = await fetch('https://graph.microsoft.com/v1.0/me', {
|
||
headers: { 'Authorization': `Bearer ${accessToken}` }
|
||
});
|
||
|
||
if (graphResponse.ok) {
|
||
const graphData = await graphResponse.json();
|
||
entraUser = { ...entraUser, ...graphData };
|
||
}
|
||
|
||
// 1️⃣ Synchroniser l’utilisateur connecté
|
||
const syncResult = await syncUserToDatabase(entraUser, accessToken);
|
||
|
||
// 2️⃣ Full sync si admin
|
||
if (syncResult?.role === 'Admin') {
|
||
console.log('Admin détecté → lancement full sync...');
|
||
await fullSyncDatabase(accessToken);
|
||
}
|
||
|
||
// 3️⃣ Vérifier groupes
|
||
const authResult = await checkUserAuthorization(entraUser.userPrincipalName, accessToken);
|
||
|
||
if (authResult.authorized) {
|
||
setUser({
|
||
id: syncResult?.localUserId || entraUser.id,
|
||
CollaborateurADId: syncResult?.localUserId,
|
||
entraUserId: entraUser.id,
|
||
name: entraUser.displayName,
|
||
prenom: entraUser.givenName || entraUser.displayName?.split(' ')[0] || '',
|
||
nom: entraUser.surname || entraUser.displayName?.split(' ')[1] || '',
|
||
email: entraUser.mail || entraUser.userPrincipalName,
|
||
userPrincipalName: entraUser.userPrincipalName,
|
||
role: syncResult?.role || 'Employe',
|
||
service: syncResult?.service || entraUser.department || 'Non défini',
|
||
jobTitle: entraUser.jobTitle,
|
||
department: entraUser.department,
|
||
officeLocation: entraUser.officeLocation,
|
||
typeContrat: syncResult?.typeContrat || '37h',
|
||
dateEntree: syncResult?.dateEntree || null,
|
||
groups: authResult.groups
|
||
});
|
||
setIsAuthorized(true);
|
||
} else {
|
||
throw new Error('Utilisateur non autorisé - pas membre des groupes requis');
|
||
}
|
||
} catch (error) {
|
||
console.error('Erreur lors de la gestion de l\'authentification:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// --- Connexion classique
|
||
const login = async (email, password) => {
|
||
try {
|
||
const response = await fetch(getApiUrl('login'), {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ email, mot_de_passe: password })
|
||
});
|
||
|
||
if (!response.ok) throw new Error('Erreur de connexion');
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
setUser({
|
||
id: data.user.id,
|
||
name: `${data.user.prenom} ${data.user.nom}`,
|
||
prenom: data.user.prenom,
|
||
nom: data.user.nom,
|
||
email: data.user.email,
|
||
role: data.user.role || 'Employe',
|
||
service: data.user.service || 'Non défini'
|
||
});
|
||
setIsAuthorized(true);
|
||
return true;
|
||
}
|
||
return false;
|
||
} catch (error) {
|
||
console.error("Erreur de connexion:", error);
|
||
return false;
|
||
}
|
||
};
|
||
|
||
// --- Connexion Office 365
|
||
const loginWithO365 = async () => {
|
||
try {
|
||
await ensureMsalInitialized();
|
||
const authResponse = await msalInstance.loginPopup(loginRequest);
|
||
await handleSuccessfulAuth(authResponse);
|
||
return true;
|
||
} catch (error) {
|
||
console.error('Erreur login Office 365:', error);
|
||
if (error.message?.includes('non autorisé')) {
|
||
throw new Error('Accès refusé: Vous n\'êtes pas membre d\'un groupe autorisé.');
|
||
}
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// --- Déconnexion
|
||
const logout = async () => {
|
||
try {
|
||
const accounts = msalInstance.getAllAccounts();
|
||
if (accounts.length > 0) {
|
||
await msalInstance.logoutPopup({ account: accounts[0] });
|
||
}
|
||
} catch (error) {
|
||
console.error('Erreur lors de la déconnexion:', error);
|
||
} finally {
|
||
setUser(null);
|
||
setUserGroups([]);
|
||
setIsAuthorized(false);
|
||
}
|
||
};
|
||
|
||
// --- Obtenir token API
|
||
const getAccessToken = async () => {
|
||
try {
|
||
await ensureMsalInitialized();
|
||
const accounts = msalInstance.getAllAccounts();
|
||
if (accounts.length === 0) throw new Error('Aucun compte connecté');
|
||
|
||
const response = await msalInstance.acquireTokenSilent({
|
||
...loginRequest,
|
||
account: accounts[0]
|
||
});
|
||
|
||
return response.accessToken;
|
||
} catch (error) {
|
||
console.error('Erreur obtention token:', error);
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const value = {
|
||
user,
|
||
userGroups,
|
||
isAuthorized,
|
||
login,
|
||
loginWithO365,
|
||
logout,
|
||
isLoading,
|
||
getAccessToken
|
||
};
|
||
|
||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||
};
|
||
|
||
export default AuthContext;
|