// Cookie utility functions for authentication const COOKIE_OPTIONS = { path: '/', secure: window.location.protocol === 'https:', sameSite: 'Lax', maxAge: 7 * 24 * 60 * 60 // 7 days in seconds } /** * Set a cookie with authentication data * @param {string} name - Cookie name * @param {string} value - Cookie value * @param {Object} options - Cookie options */ export const setCookie = (name, value, options = {}) => { try { if (!name || value === undefined || value === null) { console.warn('Invalid cookie parameters:', { name, value }) return false } const cookieOptions = { ...COOKIE_OPTIONS, ...options } let cookieString = `${name}=${encodeURIComponent(value)}` if (cookieOptions.maxAge) { cookieString += `; Max-Age=${cookieOptions.maxAge}` } if (cookieOptions.path) { cookieString += `; Path=${cookieOptions.path}` } if (cookieOptions.domain) { cookieString += `; Domain=${cookieOptions.domain}` } if (cookieOptions.secure) { cookieString += '; Secure' } if (cookieOptions.sameSite) { cookieString += `; SameSite=${cookieOptions.sameSite}` } if (cookieOptions.httpOnly) { cookieString += '; HttpOnly' } document.cookie = cookieString return true } catch (error) { console.error('Error setting cookie:', error) return false } } /** * Get a cookie value by name * @param {string} name - Cookie name * @returns {string|null} - Cookie value or null if not found */ export const getCookie = (name) => { try { if (!name) { console.warn('Cookie name is required') return null } const nameEQ = name + '=' const cookies = document.cookie.split(';') for (let i = 0; i < cookies.length; i++) { let cookie = cookies[i] while (cookie.charAt(0) === ' ') { cookie = cookie.substring(1, cookie.length) } if (cookie.indexOf(nameEQ) === 0) { return decodeURIComponent( cookie.substring(nameEQ.length, cookie.length) ) } } return null } catch (error) { console.error('Error getting cookie:', error) return null } } /** * Remove a cookie by setting it to expire in the past * @param {string} name - Cookie name * @param {Object} options - Cookie options (path and domain must match when setting) */ export const removeCookie = (name, options = {}) => { try { if (!name) { console.warn('Cookie name is required for removal') return false } const cookieOptions = { ...COOKIE_OPTIONS, ...options } setCookie(name, '', { ...cookieOptions, maxAge: -1 }) return true } catch (error) { console.error('Error removing cookie:', error) return false } } /** * Check if cookies are enabled in the browser * @returns {boolean} - True if cookies are enabled */ export const areCookiesEnabled = () => { try { setCookie('test', 'test') const enabled = getCookie('test') === 'test' removeCookie('test') return enabled } catch (e) { console.error('Error checking if cookies are enabled:', e) return false } } /** * Check if authentication cookies are expired and clean them up if needed * @returns {boolean} - True if cookies are valid and not expired */ export const validateAuthCookies = () => { try { const { token, expiresAt, user } = getAuthCookies() if (!token || !expiresAt || !user) { return false } const now = new Date() const expirationDate = new Date(expiresAt) if (expirationDate <= now) { // Cookies are expired, clean them up clearAuthCookies() return false } return true } catch (error) { console.error('Error validating auth cookies:', error) clearAuthCookies() return false } } /** * Check if authentication cookies are about to expire (within specified minutes) * @param {number} minutesBeforeExpiry - Minutes before expiry to consider "about to expire" * @returns {Object} - Object with isExpiringSoon flag and timeRemaining in milliseconds */ export const checkAuthCookiesExpiry = (minutesBeforeExpiry = 5) => { try { const { token, expiresAt, user } = getAuthCookies() if (!token || !expiresAt || !user) { return { isExpiringSoon: false, timeRemaining: 0 } } const now = new Date() const expirationDate = new Date(expiresAt) const timeRemaining = expirationDate - now const minutesRemaining = timeRemaining / (1000 * 60) return { isExpiringSoon: minutesRemaining <= minutesBeforeExpiry, timeRemaining, minutesRemaining: Math.floor(minutesRemaining) } } catch (error) { console.error('Error checking auth cookies expiry:', error) return { isExpiringSoon: false, timeRemaining: 0 } } } /** * Set up a listener for cookie changes to sync authentication state between tabs * @param {Function} onAuthChange - Callback function when auth state changes * @returns {Function} - Function to remove the listener */ export const setupCookieSync = (onAuthChange) => { const handleStorageChange = (event) => { // Check if auth-related cookies changed if ( event.key === 'authToken' || event.key === 'authExpiresAt' || event.key === 'user' ) { // Small delay to ensure cookies are updated setTimeout(() => { onAuthChange() }, 100) } } // Listen for storage events (for cross-tab communication) window.addEventListener('storage', handleStorageChange) // Also listen for cookie changes using a polling mechanism let lastCookieState = document.cookie const cookieCheckInterval = setInterval(() => { const currentCookieState = document.cookie if (currentCookieState !== lastCookieState) { lastCookieState = currentCookieState // Check if auth cookies changed const authCookies = getAuthCookies() if (authCookies.token || authCookies.expiresAt || authCookies.user) { onAuthChange() } } }, 1000) // Return cleanup function return () => { window.removeEventListener('storage', handleStorageChange) clearInterval(cookieCheckInterval) } } /** * Set authentication cookies * @param {Object} authData - Authentication data object * @returns {boolean} - True if all cookies were set successfully */ export const setAuthCookies = (authData) => { try { if (!authData) { console.warn('Auth data is required') return false } let success = true if (authData.access_token) { success = success && setCookie('authToken', authData.access_token, { maxAge: 7 * 24 * 60 * 60 }) // 7 days } if (authData.expires_at) { success = success && setCookie('authExpiresAt', authData.expires_at, { maxAge: 7 * 24 * 60 * 60 }) } if (authData.user) { const userObject = { ...authData.user, access_token: undefined, refresh_token: undefined, id_token: undefined } success = success && setCookie('user', JSON.stringify(userObject), { maxAge: 7 * 24 * 60 * 60 }) } if (!success) { console.warn('Some cookies failed to set, clearing all auth cookies') clearAuthCookies() } return success } catch (error) { console.error('Error setting auth cookies:', error) clearAuthCookies() return false } } /** * Get authentication cookies * @returns {Object} - Object containing auth data from cookies */ export const getAuthCookies = () => { return { token: getCookie('authToken'), expiresAt: getCookie('authExpiresAt'), user: getCookie('user') ? JSON.parse(getCookie('user')) : null } } /** * Clear authentication cookies */ export const clearAuthCookies = () => { removeCookie('authToken') removeCookie('authExpiresAt') removeCookie('user') }