import { browser } from '$app/environment'; // Client-side only functions export const scopes = [ 'https://www.googleapis.com/auth/gmail.send', 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/drive.readonly', 'https://www.googleapis.com/auth/spreadsheets.readonly' ]; /** * Initialize Google Auth (placeholder for client-side) */ export async function initGoogleAuth(): Promise { if (!browser) return; // Google Auth initialization is handled by the OAuth flow // No initialization needed for our server-side approach } /** * Get the Google Auth URL * @returns URL for Google OAuth */ export function getAuthUrl(): string { if (!browser) return ''; // This should be obtained from the server return '/auth/google'; } /** * Check if an access token is valid * @param accessToken - Google access token to validate * @returns True if the token is valid */ export async function isTokenValid(accessToken: string): Promise { if (!browser) return false; try { const response = await fetch(`https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=${accessToken}`); const data = await response.json(); if (response.ok && data.expires_in && data.expires_in > 0) { return true; } return false; } catch (error) { console.error('Error validating token:', error); return false; } } /** * Refresh an access token using the refresh token * @param refreshToken - Google refresh token * @returns New access token or null if failed */ export async function refreshAccessToken(refreshToken: string): Promise { try { const response = await fetch('/private/api/google/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken }) }); if (response.ok) { const data = await response.json(); return data.accessToken; } return null; } catch (error) { console.error('Error refreshing token:', error); return null; } } /** * Get Google user information * @param accessToken - Google access token * @returns User info including email, name, and picture */ export async function getUserInfo(accessToken: string): Promise<{ email: string; name: string; picture: string } | null> { try { const response = await fetch('/private/api/google/auth/userinfo', { headers: { 'Authorization': `Bearer ${accessToken}` } }); if (response.ok) { return await response.json(); } return null; } catch (error) { console.error('Error fetching user info:', error); return null; } } /** * Authenticate with Google using OAuth popup flow * @returns Authentication result with success status and tokens */ export async function authenticateWithGoogle(): Promise<{ success: boolean; refreshToken?: string; userEmail?: string; error?: string; }> { if (!browser) { return { success: false, error: 'Not in browser environment' }; } return new Promise((resolve) => { try { // Open popup window for OAuth const popup = window.open( '/auth/google', 'google-auth', 'width=500,height=600,scrollbars=yes,resizable=yes,left=' + Math.round(window.screen.width / 2 - 250) + ',top=' + Math.round(window.screen.height / 2 - 300) ); if (!popup) { resolve({ success: false, error: 'Failed to open popup window. Please allow popups for this site.' }); return; } let authCompleted = false; let popupTimer: number | null = null; // Store current timestamp to detect changes in localStorage const startTimestamp = localStorage.getItem('google_auth_timestamp') ?? '0'; // Poll localStorage for auth completion const pollInterval = setInterval(() => { try { const currentTimestamp = localStorage.getItem('google_auth_timestamp'); // If timestamp has changed, auth is complete if (currentTimestamp && currentTimestamp !== startTimestamp) { handleAuthSuccess(); } } catch (e) { console.error('Error checking auth timestamp:', e); } }, 500); // Poll every 500ms // Common handler for authentication success function handleAuthSuccess() { if (authCompleted) return; // Prevent duplicate handling authCompleted = true; // Clean up timers clearInterval(pollInterval); if (popupTimer) clearTimeout(popupTimer); // Get tokens from localStorage const refreshToken = localStorage.getItem('google_refresh_token'); const userEmail = localStorage.getItem('google_user_email'); if (refreshToken) { resolve({ success: true, refreshToken, userEmail: userEmail ?? undefined }); } else { resolve({ success: false, error: 'No refresh token found after authentication' }); } } // Clean up function to handle all cleanup in one place const cleanUp = () => { clearInterval(pollInterval); if (popupTimer) clearTimeout(popupTimer); }; // Set a timeout for initial auth check popupTimer = setTimeout(() => { if (!authCompleted) { cleanUp(); // Check if tokens were stored by the popup before it was closed const refreshToken = localStorage.getItem('google_refresh_token'); const userEmail = localStorage.getItem('google_user_email'); if (refreshToken) { resolve({ success: true, refreshToken, userEmail: userEmail ?? undefined }); } else { resolve({ success: false, error: 'Authentication timeout or cancelled' }); } } }, 30 * 1000) as unknown as number; // Final cleanup timeout setTimeout(() => { if (!authCompleted) { cleanUp(); resolve({ success: false, error: 'Authentication timeout' }); } }, 60 * 1000); } catch (error) { console.error('Error connecting to Google:', error); resolve({ success: false, error: error instanceof Error ? error.message : 'Unknown error' }); } }); } /** * Revoke a Google access token * @param accessToken - Google access token to revoke * @returns True if revocation was successful */ export async function revokeToken(accessToken: string): Promise { try { const response = await fetch('/private/api/google/auth/revoke', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ accessToken }) }); return response.ok; } catch (error) { console.error('Error revoking token:', error); return false; } }