import { google } from "googleapis"; import * as fs from "fs"; import * as path from "path"; import type { Credentials } from "google-auth-library"; import { startOAuthServer } from "./oauth-server"; import open from "open"; function saveTokensToFile(tokens: Credentials, tokenPath: string): void { // Normalize the path to ensure proper handling on all platforms const normalizedPath = path.normalize(tokenPath); // Ensure the directory exists const dirname = path.dirname(normalizedPath); if (!fs.existsSync(dirname)) { fs.mkdirSync(dirname, { recursive: true }); } fs.writeFileSync(normalizedPath, JSON.stringify(tokens)); } function loadTokensFromFile(tokenPath: string): Credentials { try { // Normalize the path const normalizedPath = path.normalize(tokenPath); return JSON.parse(fs.readFileSync(normalizedPath, "utf8")); } catch (err) { throw new Error( `Error loading token file: ${ err instanceof Error ? err.message : String(err) }` ); } } export async function createAuthClient(): Promise<any> { const oauthClientId = process.env.GOOGLE_OAUTH_CLIENT_ID; const oauthClientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET; const oauthTokenPath = process.env.GOOGLE_OAUTH_TOKEN_PATH ? path.normalize(process.env.GOOGLE_OAUTH_TOKEN_PATH) : undefined; const redirectUri = process.env.GOOGLE_OAUTH_REDIRECT_URI || "http://localhost:3001"; const clientEmail = process.env.GOOGLE_CLIENT_EMAIL; const privateKey = process.env.GOOGLE_PRIVATE_KEY; if (oauthClientId && oauthClientSecret && oauthTokenPath) { const oAuth2Client = new google.auth.OAuth2( oauthClientId, oauthClientSecret, redirectUri ); try { const tokens = loadTokensFromFile(oauthTokenPath); oAuth2Client.setCredentials(tokens); return oAuth2Client; } catch (error) { // Tokens not found or invalid, initiate OAuth flow and wait for completion await initiateOAuthFlow(); // After flow completes, load the newly saved tokens const tokens = loadTokensFromFile(oauthTokenPath); oAuth2Client.setCredentials(tokens); return oAuth2Client; } } else { // Fallback to service account if (!clientEmail || !privateKey) { throw new Error( "Authentication failed: Neither OAuth nor Service Account credentials are properly configured in environment variables." ); } return new google.auth.JWT({ email: clientEmail, key: privateKey.replace(/\\n/g, "\n"), scopes: [ "https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/gmail.modify", "https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/tasks", ], subject: process.env.GMAIL_USER_TO_IMPERSONATE, }); } } export async function initiateOAuthFlow(scopes?: string[]): Promise<void> { try { // Start the OAuth server to handle the callback const serverPromise = startOAuthServer(); // Generate and open the consent URL const authUrl = generateOAuthConsentUrl(scopes); await open(authUrl); // Wait for the server to complete the flow await serverPromise; } catch (error) { throw error; } } export function generateOAuthConsentUrl(scopes?: string[]): string { const clientId = process.env.GOOGLE_OAUTH_CLIENT_ID; const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET; const redirectUri = process.env.GOOGLE_OAUTH_REDIRECT_URI || "http://localhost:3001"; if (!clientId || !clientSecret) { throw new Error( "OAuth client ID and secret are required in environment variables" ); } const oauth2Client = new google.auth.OAuth2( clientId, clientSecret, redirectUri ); return oauth2Client.generateAuthUrl({ access_type: "offline", scope: scopes || [ "https://www.googleapis.com/auth/gmail.modify", "https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/tasks", ], prompt: "consent", }); } export async function handleOAuthCallback(code: string): Promise<void> { const clientId = process.env.GOOGLE_OAUTH_CLIENT_ID; const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET; const redirectUri = process.env.GOOGLE_OAUTH_REDIRECT_URI || "http://localhost:3001"; const tokenPath = process.env.GOOGLE_OAUTH_TOKEN_PATH; if (!clientId || !clientSecret || !tokenPath) { throw new Error( "OAuth client ID, secret, and token path are required in environment variables" ); } const oauth2Client = new google.auth.OAuth2( clientId, clientSecret, redirectUri ); const { tokens } = await oauth2Client.getToken(code); saveTokensToFile(tokens, tokenPath); }
ID: opwf6bop3j