// AuthContext - Authentication state management // Handles user login, logout, token refresh, and auth state const AuthContext = React.createContext(null); const TOKEN_KEY = 'rosa_access_token'; const REFRESH_KEY = 'rosa_refresh_token'; const USER_KEY = 'rosa_user'; function AuthProvider({ children }) { const [user, setUser] = React.useState(null); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(null); // Check for existing session on mount React.useEffect(() => { const initAuth = async () => { const token = localStorage.getItem(TOKEN_KEY); const savedUser = localStorage.getItem(USER_KEY); if (token && savedUser) { try { // Verify token is still valid by fetching user profile const response = await fetch('/api/auth/me', { headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { const userData = await response.json(); setUser(userData); // Update localStorage with fresh permissions localStorage.setItem(USER_KEY, JSON.stringify(userData)); } else if (response.status === 401) { // Try to refresh token const refreshed = await refreshToken(); if (!refreshed) { clearAuth(); } } else { clearAuth(); } } catch (err) { console.error('Auth init error:', err); clearAuth(); } } setLoading(false); }; initAuth(); }, []); const clearAuth = () => { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(REFRESH_KEY); localStorage.removeItem(USER_KEY); setUser(null); }; const refreshToken = async () => { const refresh = localStorage.getItem(REFRESH_KEY); if (!refresh) return false; try { const response = await fetch('/api/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh_token: refresh }) }); if (response.ok) { const data = await response.json(); localStorage.setItem(TOKEN_KEY, data.access_token); localStorage.setItem(REFRESH_KEY, data.refresh_token); // Fetch updated user data const userResponse = await fetch('/api/auth/me', { headers: { 'Authorization': `Bearer ${data.access_token}` } }); if (userResponse.ok) { const userData = await userResponse.json(); setUser(userData); localStorage.setItem(USER_KEY, JSON.stringify(userData)); } return true; } } catch (err) { console.error('Token refresh error:', err); } return false; }; const login = async (email, password) => { setError(null); setLoading(true); try { const formData = new URLSearchParams(); formData.append('username', email); formData.append('password', password); const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formData.toString() }); if (response.ok) { const data = await response.json(); localStorage.setItem(TOKEN_KEY, data.access_token); localStorage.setItem(REFRESH_KEY, data.refresh_token); localStorage.setItem(USER_KEY, JSON.stringify(data.user)); setUser(data.user); return { success: true }; } else { const errorData = await response.json().catch(() => ({})); const message = errorData.detail || 'Accesso fallito'; setError(message); return { success: false, error: message }; } } catch (err) { const message = 'Errore di rete. Riprova.'; setError(message); return { success: false, error: message }; } finally { setLoading(false); } }; const logout = () => { clearAuth(); }; const updateProfile = async (data) => { const token = localStorage.getItem(TOKEN_KEY); if (!token) return { success: false, error: 'Non autenticato' }; try { const response = await fetch('/api/auth/me', { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (response.ok) { const userData = await response.json(); setUser(userData); localStorage.setItem(USER_KEY, JSON.stringify(userData)); return { success: true }; } else { const errorData = await response.json().catch(() => ({})); return { success: false, error: errorData.detail || 'Aggiornamento fallito' }; } } catch (err) { return { success: false, error: 'Errore di rete' }; } }; const changePassword = async (currentPassword, newPassword) => { const token = localStorage.getItem(TOKEN_KEY); if (!token) return { success: false, error: 'Non autenticato' }; try { const response = await fetch('/api/auth/change-password', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ current_password: currentPassword, new_password: newPassword }) }); if (response.ok) { return { success: true }; } else { const errorData = await response.json().catch(() => ({})); return { success: false, error: errorData.detail || 'Modifica password fallita' }; } } catch (err) { return { success: false, error: 'Errore di rete' }; } }; const refreshUser = async () => { const token = localStorage.getItem(TOKEN_KEY); if (!token) return { success: false, error: 'Non autenticato' }; try { const response = await fetch('/api/auth/me', { headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { const userData = await response.json(); setUser(userData); localStorage.setItem(USER_KEY, JSON.stringify(userData)); return { success: true }; } else { const errorData = await response.json().catch(() => ({})); return { success: false, error: errorData.detail || 'Aggiornamento fallito' }; } } catch (err) { return { success: false, error: 'Errore di rete' }; } }; const getToken = () => localStorage.getItem(TOKEN_KEY); const isAdmin = user?.role === 'admin'; const value = { user, loading, error, isAuthenticated: !!user, isAdmin, login, logout, updateProfile, changePassword, refreshUser, getToken, refreshToken, }; return ( {children} ); } function useAuth() { const context = React.useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within an AuthProvider'); } return context; } // Make available globally for non-module scripts window.AuthContext = AuthContext; window.AuthProvider = AuthProvider; window.useAuth = useAuth;