courrier msft oauth
This commit is contained in:
parent
ab1f541b53
commit
dcad2f160d
@ -66,13 +66,15 @@ export async function POST(request: Request) {
|
|||||||
// Create credentials object for Microsoft account
|
// Create credentials object for Microsoft account
|
||||||
const credentials = {
|
const credentials = {
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
|
// Password is empty for OAuth accounts
|
||||||
|
password: '',
|
||||||
// Use Microsoft's IMAP server for Outlook/Office365
|
// Use Microsoft's IMAP server for Outlook/Office365
|
||||||
host: 'outlook.office365.com',
|
host: 'outlook.office365.com',
|
||||||
port: 993,
|
port: 993,
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
// OAuth specific fields
|
// OAuth specific fields
|
||||||
useOAuth: true,
|
useOAuth: true, // Make sure this is explicitly set
|
||||||
accessToken: tokens.access_token,
|
accessToken: tokens.access_token,
|
||||||
refreshToken: tokens.refresh_token,
|
refreshToken: tokens.refresh_token,
|
||||||
tokenExpiry: Date.now() + (tokens.expires_in * 1000),
|
tokenExpiry: Date.now() + (tokens.expires_in * 1000),
|
||||||
@ -87,6 +89,15 @@ export async function POST(request: Request) {
|
|||||||
smtp_secure: false
|
smtp_secure: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Log Microsoft authentication details
|
||||||
|
console.log(`Microsoft OAuth credentials prepared for ${userEmail}:`, {
|
||||||
|
useOAuth: credentials.useOAuth,
|
||||||
|
host: credentials.host,
|
||||||
|
hasAccessToken: !!credentials.accessToken,
|
||||||
|
hasRefreshToken: !!credentials.refreshToken,
|
||||||
|
tokenExpiry: new Date(credentials.tokenExpiry).toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
// Test connection before saving
|
// Test connection before saving
|
||||||
console.log(`Testing Microsoft OAuth connection for user ${session.user.id}`);
|
console.log(`Testing Microsoft OAuth connection for user ${session.user.id}`);
|
||||||
const testResult = await testEmailConnection(credentials);
|
const testResult = await testEmailConnection(credentials);
|
||||||
|
|||||||
80
lib/redis.ts
80
lib/redis.ts
@ -150,6 +150,10 @@ interface EmailCredentials {
|
|||||||
smtp_secure?: boolean;
|
smtp_secure?: boolean;
|
||||||
display_name?: string;
|
display_name?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
useOAuth?: boolean;
|
||||||
|
accessToken?: string;
|
||||||
|
refreshToken?: string;
|
||||||
|
tokenExpiry?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImapSessionData {
|
interface ImapSessionData {
|
||||||
@ -172,7 +176,7 @@ export async function cacheEmailCredentials(
|
|||||||
const key = KEYS.CREDENTIALS(userId, accountId);
|
const key = KEYS.CREDENTIALS(userId, accountId);
|
||||||
|
|
||||||
// Validate credentials before caching
|
// Validate credentials before caching
|
||||||
if (!credentials.email || !credentials.host || !credentials.password) {
|
if (!credentials.email || !credentials.host || (!credentials.password && !credentials.useOAuth)) {
|
||||||
console.error(`Cannot cache incomplete credentials for user ${userId}`);
|
console.error(`Cannot cache incomplete credentials for user ${userId}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -191,10 +195,15 @@ export async function cacheEmailCredentials(
|
|||||||
...(credentials.smtp_port && { smtp_port: credentials.smtp_port }),
|
...(credentials.smtp_port && { smtp_port: credentials.smtp_port }),
|
||||||
...(credentials.smtp_secure !== undefined && { smtp_secure: credentials.smtp_secure }),
|
...(credentials.smtp_secure !== undefined && { smtp_secure: credentials.smtp_secure }),
|
||||||
...(credentials.display_name && { display_name: credentials.display_name }),
|
...(credentials.display_name && { display_name: credentials.display_name }),
|
||||||
...(credentials.color && { color: credentials.color })
|
...(credentials.color && { color: credentials.color }),
|
||||||
|
// Include OAuth fields
|
||||||
|
...(credentials.useOAuth !== undefined && { useOAuth: credentials.useOAuth }),
|
||||||
|
...(credentials.accessToken && { accessToken: credentials.accessToken }),
|
||||||
|
...(credentials.refreshToken && { refreshToken: credentials.refreshToken }),
|
||||||
|
...(credentials.tokenExpiry && { tokenExpiry: credentials.tokenExpiry })
|
||||||
};
|
};
|
||||||
|
|
||||||
// Encrypt password
|
// Encrypt password if provided
|
||||||
if (credentials.password) {
|
if (credentials.password) {
|
||||||
try {
|
try {
|
||||||
const encrypted = encryptData(credentials.password);
|
const encrypted = encryptData(credentials.password);
|
||||||
@ -202,12 +211,8 @@ export async function cacheEmailCredentials(
|
|||||||
secureCredentials.encryptedPassword = encrypted;
|
secureCredentials.encryptedPassword = encrypted;
|
||||||
} catch (encryptError) {
|
} catch (encryptError) {
|
||||||
console.error(`Failed to encrypt password for user ${userId}:`, encryptError);
|
console.error(`Failed to encrypt password for user ${userId}:`, encryptError);
|
||||||
// Don't proceed with caching if encryption fails
|
// Continue anyway since we might have OAuth tokens
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.warn(`No password provided for user ${userId}, skipping credential caching`);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await redis.set(key, JSON.stringify(secureCredentials), 'EX', TTL.CREDENTIALS);
|
await redis.set(key, JSON.stringify(secureCredentials), 'EX', TTL.CREDENTIALS);
|
||||||
@ -236,35 +241,44 @@ export async function getEmailCredentials(
|
|||||||
|
|
||||||
const creds = JSON.parse(credStr) as EmailCredentials;
|
const creds = JSON.parse(credStr) as EmailCredentials;
|
||||||
|
|
||||||
if (!creds.encryptedPassword) {
|
let password: string | undefined;
|
||||||
console.warn(`No encrypted password found for user ${userId}`);
|
|
||||||
return null;
|
// Handle OAuth accounts (they might not have a password)
|
||||||
|
if (creds.encryptedPassword) {
|
||||||
|
try {
|
||||||
|
// Decrypt the password
|
||||||
|
password = decryptData(creds.encryptedPassword);
|
||||||
|
} catch (decryptError) {
|
||||||
|
console.error(`Failed to decrypt password for user ${userId}:`, decryptError);
|
||||||
|
// For OAuth accounts, we can continue without a password
|
||||||
|
if (!creds.useOAuth) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// Return the full credentials with decrypted password if available
|
||||||
// Decrypt the password
|
const result: EmailCredentials = {
|
||||||
const password = decryptData(creds.encryptedPassword);
|
email: creds.email,
|
||||||
|
host: creds.host,
|
||||||
|
port: creds.port,
|
||||||
|
secure: creds.secure ?? true,
|
||||||
|
...(password && { password }),
|
||||||
|
...(creds.smtp_host && { smtp_host: creds.smtp_host }),
|
||||||
|
...(creds.smtp_port && { smtp_port: creds.smtp_port }),
|
||||||
|
...(creds.smtp_secure !== undefined && { smtp_secure: creds.smtp_secure }),
|
||||||
|
...(creds.display_name && { display_name: creds.display_name }),
|
||||||
|
...(creds.color && { color: creds.color }),
|
||||||
|
// Include OAuth fields
|
||||||
|
...(creds.useOAuth !== undefined && { useOAuth: creds.useOAuth }),
|
||||||
|
...(creds.accessToken && { accessToken: creds.accessToken }),
|
||||||
|
...(creds.refreshToken && { refreshToken: creds.refreshToken }),
|
||||||
|
...(creds.tokenExpiry && { tokenExpiry: creds.tokenExpiry })
|
||||||
|
};
|
||||||
|
|
||||||
// Return the full credentials with decrypted password
|
return result;
|
||||||
return {
|
|
||||||
email: creds.email,
|
|
||||||
password,
|
|
||||||
host: creds.host,
|
|
||||||
port: creds.port,
|
|
||||||
secure: creds.secure ?? true,
|
|
||||||
// Include the extended fields if they exist in the cache
|
|
||||||
...(creds.smtp_host && { smtp_host: creds.smtp_host }),
|
|
||||||
...(creds.smtp_port && { smtp_port: creds.smtp_port }),
|
|
||||||
...(creds.smtp_secure !== undefined && { smtp_secure: creds.smtp_secure }),
|
|
||||||
...(creds.display_name && { display_name: creds.display_name }),
|
|
||||||
...(creds.color && { color: creds.color })
|
|
||||||
};
|
|
||||||
} catch (decryptError) {
|
|
||||||
console.error(`Failed to decrypt password for user ${userId}:`, decryptError);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error retrieving credentials for user ${userId}:`, error);
|
console.error(`Error getting credentials for user ${userId}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -386,30 +386,27 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
|
|||||||
|
|
||||||
let authParams: any;
|
let authParams: any;
|
||||||
|
|
||||||
// Check if we should use OAuth
|
// Check if we have valid OAuth tokens
|
||||||
if (extendedCreds.useOAuth && extendedCreds.accessToken) {
|
if (extendedCreds.useOAuth && extendedCreds.accessToken) {
|
||||||
// Configure for XOAUTH2
|
console.log(`Using XOAUTH2 authentication for ${connectionKey} (OAuth enabled)`);
|
||||||
console.log(`Using XOAUTH2 authentication for ${connectionKey}`);
|
|
||||||
|
|
||||||
// Generate the XOAUTH2 token
|
|
||||||
const xoauth2Token = createXOAuth2Token(extendedCreds.email, extendedCreds.accessToken);
|
|
||||||
|
|
||||||
// Set auth parameters for ImapFlow
|
// Set auth parameters for ImapFlow
|
||||||
authParams = {
|
authParams = {
|
||||||
user: extendedCreds.email,
|
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
|
accessToken: extendedCreds.accessToken
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(`XOAUTH2 auth configured for ${connectionKey}`);
|
console.log(`XOAUTH2 auth configured for ${connectionKey}`);
|
||||||
} else {
|
} else if (extendedCreds.password) {
|
||||||
// Use regular password authentication
|
// Use regular password authentication
|
||||||
console.log(`Using password authentication for ${connectionKey}`);
|
console.log(`Using password authentication for ${connectionKey} (OAuth not enabled or no token)`);
|
||||||
authParams = {
|
authParams = {
|
||||||
user: extendedCreds.email,
|
user: extendedCreds.email,
|
||||||
pass: extendedCreds.password
|
pass: extendedCreds.password
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
// No authentication method available
|
||||||
|
throw new Error(`No authentication method available for ${connectionKey} - need either password or OAuth token`);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Creating ImapFlow client for ${connectionKey} with authentication type: ${extendedCreds.useOAuth ? 'OAuth' : 'Password'}`);
|
console.log(`Creating ImapFlow client for ${connectionKey} with authentication type: ${extendedCreds.useOAuth ? 'OAuth' : 'Password'}`);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user