ai-wpa/app/api/auth/google/callback/route.ts

114 lines
3.4 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { getGoogleUser } from '@/lib/google-oauth'
import { generateTokens } from '@/lib/jwt'
import connectDB from '@/lib/mongodb'
import { User } from '@/models/user'
import { appConfig } from '@/lib/env'
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const code = searchParams.get('code')
const state = searchParams.get('state')
const error = searchParams.get('error')
// Handle OAuth errors
if (error) {
return NextResponse.redirect(
new URL(`/auth?error=${encodeURIComponent(error)}`, appConfig.appUrl)
)
}
// Validate required parameters
if (!code || state !== 'google_oauth') {
return NextResponse.redirect(new URL('/auth?error=invalid_oauth_callback', appConfig.appUrl))
}
// Get user info from Google
const googleUser = await getGoogleUser(code)
// Connect to database
await connectDB()
// Check if user exists
let user = await User.findOne({
$or: [
{ email: googleUser.email.toLowerCase() },
{ providerId: googleUser.id, provider: 'google' },
],
})
if (!user) {
// Create new user
user = new User({
email: googleUser.email.toLowerCase(),
name: googleUser.name,
provider: 'google',
providerId: googleUser.id,
avatar: googleUser.picture || undefined,
isVerified: googleUser.verified_email,
lastLogin: new Date(),
})
} else {
// Update existing user with Google info (preserve original provider)
// Only link Google if user doesn't already have a local account
if (user.provider !== 'local') {
user.provider = 'google'
user.providerId = googleUser.id
} else {
// For local users, just add Google info without changing provider
if (!user.providerId) {
user.providerId = googleUser.id
}
}
// Update other info safely
user.name = googleUser.name
user.isVerified = googleUser.verified_email || user.isVerified
user.lastLogin = new Date()
// Update avatar only if user doesn't have one
if (googleUser.picture && !user.avatar) {
user.avatar = googleUser.picture
}
}
// Generate tokens
const { accessToken, refreshToken } = generateTokens({
userId: user._id.toString(),
email: user.email,
role: user.role,
})
// Update user's refresh token
user.refreshToken = refreshToken
await user.save()
// Create redirect response using the public app URL
const redirectURL = new URL('/dashboard', appConfig.appUrl)
const response = NextResponse.redirect(redirectURL)
// Set HTTP-only cookies
response.cookies.set('accessToken', accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 15 * 60, // 15 minutes
path: '/',
})
response.cookies.set('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60, // 7 days
path: '/',
})
return response
} catch (error) {
console.error('Google OAuth callback error:', error)
return NextResponse.redirect(new URL('/auth?error=oauth_callback_failed', appConfig.appUrl))
}
}