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 {children}; }; export default AuthContext;