From 49dd79e3bade3b9f583f995efc701aa5dbe0f918 Mon Sep 17 00:00:00 2001 From: alma Date: Fri, 2 May 2025 09:41:19 +0200 Subject: [PATCH] courrier msft oauth --- lib/services/email-service.ts | 126 +++++++++++++++++++++----------- lib/services/microsoft-oauth.ts | 7 +- 2 files changed, 91 insertions(+), 42 deletions(-) diff --git a/lib/services/email-service.ts b/lib/services/email-service.ts index 8ef6abe6..539f0d3f 100644 --- a/lib/services/email-service.ts +++ b/lib/services/email-service.ts @@ -19,7 +19,10 @@ import { } from '@/lib/redis'; import { EmailCredentials, EmailMessage, EmailAddress, EmailAttachment } from '@/lib/types'; import { ensureFreshToken } from './token-refresh'; -import { createXOAuth2Token } from './microsoft-oauth'; +import { createXOAuth2Token, refreshAccessToken as refreshMicrosoftAccessToken } from './microsoft-oauth'; +import { MailCredentials } from '@prisma/client'; +import Redis from 'ioredis'; +import { getRedisClient } from '../redis'; // Define EmailCredentials interface with OAuth properties interface EmailCredentialsExtended extends EmailCredentials { @@ -346,36 +349,57 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey // Cast to extended type const extendedCreds = credentials as EmailCredentialsExtended; - // Configure auth - let auth: any = { - user: extendedCreds.email - }; + let authParams: any; + // Check if we should use OAuth if (extendedCreds.useOAuth && extendedCreds.accessToken) { - // For OAuth, create the proper XOAUTH2 token format - const xoauth2 = createXOAuth2Token(extendedCreds.email, extendedCreds.accessToken); - auth.xoauth2 = xoauth2; + // Configure for XOAUTH2 console.log(`Using XOAUTH2 authentication for ${connectionKey}`); + + // Generate the XOAUTH2 token + const xoauth2Token = createXOAuth2Token(extendedCreds.email, extendedCreds.accessToken); + + // Set auth parameters for ImapFlow + authParams = { + user: extendedCreds.email, + // IMPORTANT: For ImapFlow, we need to provide the accessToken directly + // The library will handle converting it to XOAUTH2 format internally + accessToken: extendedCreds.accessToken + }; + + console.log(`XOAUTH2 auth configured for ${connectionKey}`); } else { - auth.pass = extendedCreds.password; + // Use regular password authentication console.log(`Using password authentication for ${connectionKey}`); + authParams = { + user: extendedCreds.email, + pass: extendedCreds.password + }; } + console.log(`Creating ImapFlow client for ${connectionKey} with authentication type: ${extendedCreds.useOAuth ? 'OAuth' : 'Password'}`); + const client = new ImapFlow({ host: extendedCreds.host, port: extendedCreds.port, secure: extendedCreds.secure ?? true, - auth, + auth: authParams, logger: false, emitLogs: false, tls: { rejectUnauthorized: false }, - // Connection timeout settings - disableAutoIdle: false // Keep idle to auto-refresh connection + disableAutoIdle: false }); - await client.connect(); + try { + console.log(`Connecting to IMAP server: ${extendedCreds.host}:${extendedCreds.port}`); + await client.connect(); + console.log(`Successfully connected to IMAP server for ${connectionKey}`); + } catch (error) { + console.error(`Failed to connect to IMAP server for ${connectionKey}:`, error); + throw error; + } // Add error handler client.on('error', (err) => { @@ -979,9 +1003,21 @@ export async function toggleEmailFlag( } } -/** - * Send an email - */ +// Define EmailContent interface +interface EmailContent { + to: string; + cc?: string; + bcc?: string; + subject: string; + plainText: string; + htmlContent: string; + attachments?: Array<{ + filename: string; + content: string; + contentType: string; + }>; +} + export async function sendEmail( userId: string, emailData: { @@ -1005,33 +1041,32 @@ export async function sendEmail( error: 'No email credentials found' }; } - + // Cast to extended type const extendedCreds = credentials as EmailCredentialsExtended; // Configure SMTP auth based on OAuth or password - let auth: any = { - user: extendedCreds.email - }; + const smtpAuth = extendedCreds.useOAuth && extendedCreds.accessToken + ? { + type: 'OAuth2', + user: extendedCreds.email, + accessToken: extendedCreds.accessToken + } + : { + user: extendedCreds.email, + pass: extendedCreds.password + }; - if (extendedCreds.useOAuth && extendedCreds.accessToken) { - // For OAuth, use the XOAuth2 format - auth.type = 'OAuth2'; - auth.accessToken = extendedCreds.accessToken; - } else { - auth.pass = extendedCreds.password; - } - - // Create SMTP transporter with user's SMTP settings if available + // Create SMTP transporter with user's SMTP settings const transporter = nodemailer.createTransport({ host: extendedCreds.smtp_host || 'smtp.infomaniak.com', port: extendedCreds.smtp_port || 587, secure: extendedCreds.smtp_secure || false, - auth, + auth: smtpAuth, tls: { rejectUnauthorized: false } - } as any); + } as nodemailer.TransportOptions); try { const info = await transporter.sendMail({ @@ -1054,6 +1089,7 @@ export async function sendEmail( messageId: info.messageId }; } catch (error) { + console.error('Failed to send email:', error); return { success: false, error: error instanceof Error ? error.message : 'Unknown error' @@ -1106,25 +1142,32 @@ export async function testEmailConnection(credentials: EmailCredentials): Promis console.log(`Testing IMAP connection to ${extendedCreds.host}:${extendedCreds.port} for ${extendedCreds.email}`); // Configure auth based on whether we're using OAuth or password - let auth: any = { - user: extendedCreds.email - }; + let authParams: any; if (extendedCreds.useOAuth && extendedCreds.accessToken) { - // For OAuth, create the proper XOAUTH2 token format - const xoauth2 = createXOAuth2Token(extendedCreds.email, extendedCreds.accessToken); - auth.xoauth2 = xoauth2; console.log('Using XOAUTH2 authentication mechanism'); + + // For OAuth, pass the accessToken directly to ImapFlow + authParams = { + user: extendedCreds.email, + accessToken: extendedCreds.accessToken + }; + + // Log the token length to verify it exists + console.log(`Access token available (length: ${extendedCreds.accessToken.length})`); } else { - auth.pass = extendedCreds.password; console.log('Using password authentication mechanism'); + authParams = { + user: extendedCreds.email, + pass: extendedCreds.password + }; } const client = new ImapFlow({ host: extendedCreds.host, port: extendedCreds.port, secure: extendedCreds.secure ?? true, - auth, + auth: authParams, logger: false, tls: { rejectUnauthorized: false @@ -1148,8 +1191,9 @@ export async function testEmailConnection(credentials: EmailCredentials): Promis console.log(`Testing SMTP connection to ${extendedCreds.smtp_host}:${extendedCreds.smtp_port}`); // Configure SMTP auth based on OAuth or password - const smtpAuth = extendedCreds.useOAuth + const smtpAuth = extendedCreds.useOAuth && extendedCreds.accessToken ? { + type: 'OAuth2', user: extendedCreds.email, accessToken: extendedCreds.accessToken } @@ -1166,7 +1210,7 @@ export async function testEmailConnection(credentials: EmailCredentials): Promis tls: { rejectUnauthorized: false } - }); + } as nodemailer.TransportOptions); await transporter.verify(); console.log(`SMTP connection successful for ${extendedCreds.email}`); diff --git a/lib/services/microsoft-oauth.ts b/lib/services/microsoft-oauth.ts index 19d9b79b..58e8b47c 100644 --- a/lib/services/microsoft-oauth.ts +++ b/lib/services/microsoft-oauth.ts @@ -150,6 +150,11 @@ export async function refreshAccessToken(refreshToken: string): Promise<{ * Create special XOAUTH2 string for IMAP authentication */ export function createXOAuth2Token(email: string, accessToken: string): string { + // This creates the XOAUTH2 token in the required format for ImapFlow + // Format: user=\x01auth=Bearer \x01\x01 const auth = `user=${email}\x01auth=Bearer ${accessToken}\x01\x01`; - return Buffer.from(auth).toString('base64'); + const base64Auth = Buffer.from(auth).toString('base64'); + + console.log('Generated XOAUTH2 token (length):', base64Auth.length); + return base64Auth; } \ No newline at end of file