import { signInWithEmailAndPassword, signOut, onAuthStateChanged, setPersistence, browserLocalPersistence, sendPasswordResetEmail, confirmPasswordReset, } from "firebase/auth"; import type { User, AuthError } from "firebase/auth"; import { app} from "../features/auth/firebase" import { getAuth } from "firebase/auth"; const auth = getAuth(app); // Token refresh interval tracking let tokenRefreshInterval: number | null = null; // Constants for session management const TOKEN_REFRESH_INTERVAL = 40 * 60 * 1000; // Refresh token every 40 minutes (Firebase ID tokens expire in 1 hour) /** * Initialize Firebase Auth persistence * Ensures user session persists across page refreshes */ export const initializeAuthPersistence = async () => { try { await setPersistence(auth, browserLocalPersistence); console.log("Auth persistence initialized with localStorage"); } catch (error) { console.error("Error setting auth persistence:", error); } }; /** * Refresh the current user's ID token to maintain session validity * Firebase automatically refreshes tokens, but we can force a refresh to ensure validity * @returns Promise - true if refresh successful, false otherwise */ export const refreshUserToken = async (): Promise => { try { const currentUser = auth.currentUser; if (currentUser) { await currentUser.getIdTokenResult(true); // Force refresh console.log("Token refreshed successfully"); return true; } return false; } catch (error) { console.error("Error refreshing token:", error); return false; } }; /** * Start automatic token refresh mechanism * Refreshes token periodically to prevent unexpected logouts */ export const startTokenRefreshTimer = () => { // Clear any existing interval if (tokenRefreshInterval) { clearInterval(tokenRefreshInterval); } // Set up auto-refresh interval tokenRefreshInterval = window.setInterval(async () => { const currentUser = auth.currentUser; if (currentUser) { await refreshUserToken(); } else { // If no user, stop the refresh timer stopTokenRefreshTimer(); } }, TOKEN_REFRESH_INTERVAL); }; /** * Stop the automatic token refresh timer */ export const stopTokenRefreshTimer = () => { if (tokenRefreshInterval) { clearInterval(tokenRefreshInterval); tokenRefreshInterval = null; } }; /** * Login user with email and password */ export const loginWithEmail = async (email: string, password: string) => { try { const userCredential = await signInWithEmailAndPassword(auth, email, password); return { success: true, user: userCredential.user, error: null, }; } catch (error) { const authError = error as AuthError; return { success: false, user: null, error: getAuthErrorMessage(authError.code), }; } }; /** * Sign out the current user * Clears session data, local storage, and stops token refresh */ export const logout = async () => { try { // Stop token refresh interval stopTokenRefreshTimer(); // Clear any session-related data from localStorage localStorage.removeItem('lastActivityTime'); localStorage.removeItem('sessionStartTime'); // Sign out from Firebase await signOut(auth); // Clear any other app-specific session data if needed sessionStorage.clear(); console.log("User logged out successfully"); return { success: true }; } catch (error) { console.error("Error during logout:", error); return { success: false, error: (error as Error).message }; } }; /** * Send password reset email */ export const sendPasswordReset = async (email: string) => { try { await sendPasswordResetEmail(auth, email); return { success: true }; } catch (error) { const authError = error as AuthError; return { success: false, error: getAuthErrorMessage(authError.code) }; } }; /** * Reset password with code and new password * Used after user clicks the link in the reset email */ export const resetPassword = async (code: string, newPassword: string) => { try { await confirmPasswordReset(auth, code, newPassword); return { success: true }; } catch (error) { const authError = error as AuthError; return { success: false, error: getAuthErrorMessage(authError.code) }; } }; /** * Subscribe to auth state changes * Sets up token refresh timer when user logs in, stops it when logs out * Returns unsubscribe function */ export const subscribeToAuthState = (callback: (user: User | null) => void) => { return onAuthStateChanged(auth, (user) => { if (user) { // User logged in - start token refresh startTokenRefreshTimer(); // Update last activity time localStorage.setItem('lastActivityTime', Date.now().toString()); localStorage.setItem('sessionStartTime', Date.now().toString()); } else { // User logged out - stop token refresh stopTokenRefreshTimer(); } callback(user); }); }; /** * Get current user synchronously */ export const getCurrentUser = () => { return auth.currentUser; }; /** * Convert Firebase error codes to user-friendly messages */ const getAuthErrorMessage = (errorCode: string): string => { const errorMessages: Record = { "auth/invalid-email": "Invalid email address format.", "auth/user-disabled": "This user account has been disabled.", "auth/user-not-found": "No account found with this email address.", "auth/wrong-password": "Invalid email or password.", "auth/invalid-credential": "Invalid email or password.", "auth/too-many-requests": "Too many login attempts. Please try again later.", "auth/operation-not-allowed": "Login is currently disabled. Please try again later.", "auth/network-request-failed": "Network error. Please check your connection.", "auth/invalid-action-code": "This password reset link is invalid or has expired.", "auth/expired-action-code": "This password reset link has expired. Please request a new one.", "auth/weak-password": "Password is too weak. Please choose a stronger password.", }; return errorMessages[errorCode] || "An error occurred. Please try again."; }; export { app };