// src/hooks/useAuth.js import { useState, useContext, createContext, useCallback, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { login as apiLogin, logout as apiLogout, refreshToken as apiRefreshToken } from '../services/auth'; import { useLocalStorage } from './useLocalStorage'; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [authTokens, setAuthTokens] = useLocalStorage('authTokens', null); const [userType, setUserType] = useLocalStorage('userType', 'Enthusiast'); const [isRefreshing, setIsRefreshing] = useState(false); const navigate = useNavigate(); const parseJwt = (token) => { try { return JSON.parse(atob(token.split('.')[1])); } catch (e) { return null; } }; const login = useCallback(async (username, password) => { try { const { access_token, refresh_token } = await apiLogin(username, password); setAuthTokens({ access: access_token, refresh: refresh_token }); setUser(username); return { success: true }; } catch (error) { return { success: false, error: error.message }; } }, [setAuthTokens]); const logout = useCallback(async () => { try { if (authTokens?.access) { await apiLogout(authTokens.access); } setAuthTokens(null); setUser(null); navigate('/login'); } catch (error) { console.error('Logout error:', error); // Even if logout API fails, clear local tokens setAuthTokens(null); setUser(null); } }, [authTokens, setAuthTokens, navigate]); const refreshToken = useCallback(async () => { if (!authTokens?.refresh || isRefreshing) return; setIsRefreshing(true); try { const { access_token, refresh_token } = await apiRefreshToken({ refresh_token: authTokens.refresh }); setAuthTokens({ access: access_token, refresh: refresh_token }); return access_token; } catch (error) { console.error('Token refresh failed:', error); logout(); // Full logout if refresh fails throw error; } finally { setIsRefreshing(false); } }, [authTokens, isRefreshing, setAuthTokens, logout]); // Auto-refresh token when it's about to expire useEffect(() => { const refreshInterval = setInterval(() => { if (authTokens?.access) { const { exp } = parseJwt(authTokens.access); // Refresh token if it expires in less than 5 minutes if (exp * 1000 - Date.now() < 300000) { refreshToken(); } } }, 60000); // Check every minute return () => clearInterval(refreshInterval); }, [authTokens, refreshToken]); const getAccessToken = useCallback(async () => { if (!authTokens?.access) return null; const { exp } = parseJwt(authTokens.access); if (exp * 1000 - Date.now() < 30000) { // If expires in <30 seconds return await refreshToken(); } return authTokens.access; }, [authTokens, refreshToken]); const value = { user, authTokens, userType, setUserType, isAuthenticated: !!authTokens?.access, isRefreshing, login, logout, refreshToken, getAccessToken }; return {children}; }; export const useAuth = () => { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within an AuthProvider'); } return context; };