125 lines
3.9 KiB
TypeScript
125 lines
3.9 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { getServerSession } from 'next-auth';
|
|
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
|
|
import { exchangeCodeForTokens } from '@/lib/services/microsoft-oauth';
|
|
import { prisma } from '@/lib/prisma';
|
|
import { testEmailConnection, saveUserEmailCredentials } from '@/lib/services/email-service';
|
|
import { invalidateFolderCache } from '@/lib/redis';
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
// Authenticate user
|
|
const session = await getServerSession(authOptions);
|
|
if (!session?.user?.id) {
|
|
return NextResponse.json(
|
|
{ error: 'Unauthorized' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
// Parse request body
|
|
const body = await request.json();
|
|
const { code, state } = body;
|
|
|
|
if (!code || !state) {
|
|
return NextResponse.json(
|
|
{ error: 'Missing required parameters' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validate state parameter to prevent CSRF
|
|
try {
|
|
const decodedState = JSON.parse(Buffer.from(state, 'base64').toString());
|
|
|
|
// Check if state contains valid userId and is not expired (10 minutes)
|
|
if (decodedState.userId !== session.user.id ||
|
|
Date.now() - decodedState.timestamp > 10 * 60 * 1000) {
|
|
return NextResponse.json(
|
|
{ error: 'Invalid or expired state parameter' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
} catch (e) {
|
|
return NextResponse.json(
|
|
{ error: 'Invalid state parameter' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Exchange code for tokens
|
|
const tokens = await exchangeCodeForTokens(code);
|
|
|
|
// Extract user email from token (would require token decoding in production)
|
|
// For this implementation, we'll use a temporary email
|
|
const userEmail = `${session.user.email || 'user'}@microsoft.com`;
|
|
|
|
// Create credentials object for Microsoft account
|
|
const credentials = {
|
|
email: userEmail,
|
|
// Use Microsoft's IMAP server for Outlook/Office365
|
|
host: 'outlook.office365.com',
|
|
port: 993,
|
|
secure: true,
|
|
|
|
// OAuth specific fields
|
|
useOAuth: true,
|
|
accessToken: tokens.access_token,
|
|
refreshToken: tokens.refresh_token,
|
|
tokenExpiry: Date.now() + (tokens.expires_in * 1000),
|
|
|
|
// Optional fields
|
|
display_name: `Microsoft ${userEmail}`,
|
|
color: '#0078D4', // Microsoft blue
|
|
|
|
// SMTP settings for Microsoft
|
|
smtp_host: 'smtp.office365.com',
|
|
smtp_port: 587,
|
|
smtp_secure: false
|
|
};
|
|
|
|
// Test connection before saving
|
|
console.log(`Testing Microsoft OAuth connection for user ${session.user.id}`);
|
|
const testResult = await testEmailConnection(credentials);
|
|
|
|
if (!testResult.imap) {
|
|
return NextResponse.json(
|
|
{ error: `Connection test failed: ${testResult.error || 'Could not connect to Microsoft IMAP server'}` },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Save credentials to database and cache
|
|
console.log(`Saving Microsoft account for user: ${session.user.id}`);
|
|
await saveUserEmailCredentials(session.user.id, userEmail, credentials);
|
|
|
|
// Fetch the created account from the database
|
|
const createdAccount = await prisma.mailCredentials.findFirst({
|
|
where: { userId: session.user.id, email: userEmail },
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
display_name: true,
|
|
color: true,
|
|
}
|
|
});
|
|
|
|
// Invalidate any existing folder caches
|
|
await invalidateFolderCache(session.user.id, userEmail, '*');
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
account: createdAccount,
|
|
message: 'Microsoft account added successfully'
|
|
});
|
|
} catch (error) {
|
|
console.error('Error processing Microsoft callback:', error);
|
|
return NextResponse.json(
|
|
{
|
|
error: 'Failed to process Microsoft authentication',
|
|
details: error instanceof Error ? error.message : 'Unknown error'
|
|
},
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|