114 lines
3.4 KiB
TypeScript
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))
|
|
}
|
|
}
|