/** * Auth Context * * Provides authentication state and methods to the application */ 'use client'; import React, { createContext, useContext, useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { login as apiLogin, register as apiRegister, logout as apiLogout, getCurrentUser, } from '../api/auth'; import { getAuthToken } from '../api/client'; import type { UserPayload } from '@/types/api'; interface AuthContextType { user: UserPayload | null; loading: boolean; login: (email: string, password: string) => Promise; register: (data: { email: string; password: string; firstName: string; lastName: string; organizationId: string; }) => Promise; logout: () => Promise; refreshUser: () => Promise; isAuthenticated: boolean; } const AuthContext = createContext(undefined); export function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const router = useRouter(); // Helper function to check if user is authenticated const isAuthenticated = () => { return !!getAuthToken(); }; // Helper function to get stored user const getStoredUser = (): UserPayload | null => { if (typeof window === 'undefined') return null; const storedUser = localStorage.getItem('user'); return storedUser ? JSON.parse(storedUser) : null; }; useEffect(() => { // Check if user is already logged in const checkAuth = async () => { try { if (isAuthenticated()) { // Try to fetch current user from API (will auto-refresh token if expired) try { const currentUser = await getCurrentUser(); setUser(currentUser); // Update stored user localStorage.setItem('user', JSON.stringify(currentUser)); } catch (apiError) { console.error('Failed to fetch user from API:', apiError); // If API fails after token refresh attempt, clear everything if (typeof window !== 'undefined') { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); localStorage.removeItem('user'); } setUser(null); } } } catch (error) { console.error('Auth check failed, clearing tokens:', error); // Token invalid or no user data, clear storage if (typeof window !== 'undefined') { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); localStorage.removeItem('user'); } setUser(null); } finally { setLoading(false); } }; checkAuth(); // Check token validity every 5 minutes and refresh if needed const tokenCheckInterval = setInterval(async () => { if (isAuthenticated()) { try { // This will automatically refresh the token if it's expired (via API client) await getCurrentUser(); } catch (error) { console.error('Token validation failed:', error); // If token refresh fails, user will be redirected to login by the API client } } }, 5 * 60 * 1000); // 5 minutes return () => clearInterval(tokenCheckInterval); }, []); const login = async (email: string, password: string) => { try { const response = await apiLogin({ email, password }); // Fetch complete user profile after login const currentUser = await getCurrentUser(); setUser(currentUser); // Store user in localStorage if (typeof window !== 'undefined') { localStorage.setItem('user', JSON.stringify(currentUser)); } router.push('/dashboard'); } catch (error) { throw error; } }; const register = async (data: { email: string; password: string; firstName: string; lastName: string; organizationId: string; }) => { try { const response = await apiRegister(data); // Fetch complete user profile after registration const currentUser = await getCurrentUser(); setUser(currentUser); // Store user in localStorage if (typeof window !== 'undefined') { localStorage.setItem('user', JSON.stringify(currentUser)); } router.push('/dashboard'); } catch (error) { throw error; } }; const logout = async () => { try { await apiLogout(); } finally { setUser(null); // Clear user from localStorage if (typeof window !== 'undefined') { localStorage.removeItem('user'); } router.push('/login'); } }; const refreshUser = async () => { try { const currentUser = await getCurrentUser(); setUser(currentUser); if (typeof window !== 'undefined') { localStorage.setItem('user', JSON.stringify(currentUser)); } } catch (error) { console.error('Failed to refresh user:', error); } }; const value = { user, loading, login, register, logout, refreshUser, isAuthenticated: !!user, }; return {children}; } export function useAuth() { const context = useContext(AuthContext); if (context === undefined) { throw new Error('useAuth must be used within an AuthProvider'); } return context; }