courrier msft oauth

This commit is contained in:
alma 2025-05-02 09:41:19 +02:00
parent b97c2310b8
commit 49dd79e3ba
2 changed files with 91 additions and 42 deletions

View File

@ -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}`);

View File

@ -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=<email>\x01auth=Bearer <token>\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;
}