import { query, execute, insertReturning } from '../../../utils/db' import { getClientIp } from '../../../utils/ip' import { createSession, setSessionCookie } from '../../../utils/session' /** * Google OAuth 콜백 * GET /api/auth/google/callback */ export default defineEventHandler(async (event) => { const config = useRuntimeConfig() const params = getQuery(event) const clientIp = getClientIp(event) const userAgent = getHeader(event, 'user-agent') || null const code = params.code as string const state = params.state as string const error = params.error as string if (error) { return sendRedirect(event, '/login?error=oauth_denied') } // State 검증 const savedState = getCookie(event, 'oauth_state') if (!savedState || savedState !== state) { return sendRedirect(event, '/login?error=invalid_state') } deleteCookie(event, 'oauth_state') const clientId = config.googleClientId || process.env.GOOGLE_CLIENT_ID const clientSecret = config.googleClientSecret || process.env.GOOGLE_CLIENT_SECRET const redirectUri = config.googleRedirectUri || process.env.GOOGLE_REDIRECT_URI || 'http://localhost:3000/api/auth/google/callback' if (!clientId || !clientSecret) { return sendRedirect(event, '/login?error=oauth_not_configured') } try { // 토큰 교환 const tokenRes = await fetch('https://oauth2.googleapis.com/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ code, client_id: clientId, client_secret: clientSecret, redirect_uri: redirectUri, grant_type: 'authorization_code' }) }) const tokenData = await tokenRes.json() if (!tokenData.access_token) { console.error('Token error:', tokenData) return sendRedirect(event, '/login?error=token_failed') } // 사용자 정보 조회 const userRes = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', { headers: { Authorization: `Bearer ${tokenData.access_token}` } }) const googleUser = await userRes.json() if (!googleUser.email) { return sendRedirect(event, '/login?error=no_email') } const googleId = googleUser.id const googleEmail = googleUser.email.toLowerCase() const googleName = googleUser.name || googleEmail.split('@')[0] // 기존 사용자 조회 (이메일 기준) let employees = await query(` SELECT * FROM wr_employee_info WHERE employee_email = $1 `, [googleEmail]) let employee = employees[0] if (employee) { // 기존 사용자 - Google 정보 연결 await execute(` UPDATE wr_employee_info SET google_id = $1, google_email = $2, google_linked_at = NOW(), google_access_token = $3, google_refresh_token = $4, google_token_expires_at = NOW() + INTERVAL '${tokenData.expires_in} seconds', last_login_at = NOW(), last_login_ip = $5, updated_at = NOW() WHERE employee_id = $6 `, [googleId, googleEmail, tokenData.access_token, tokenData.refresh_token || null, clientIp, employee.employee_id]) } else { // 신규 사용자 자동 등록 employee = await insertReturning(` INSERT INTO wr_employee_info ( employee_name, employee_email, google_id, google_email, google_linked_at, google_access_token, google_refresh_token, google_token_expires_at, last_login_at, last_login_ip, created_ip, created_email ) VALUES ($1, $2, $3, $2, NOW(), $4, $5, NOW() + INTERVAL '${tokenData.expires_in} seconds', NOW(), $6, $6, $2) RETURNING * `, [googleName, googleEmail, googleId, tokenData.access_token, tokenData.refresh_token || null, clientIp]) } // 로그인 이력 추가 const loginHistory = await insertReturning(` INSERT INTO wr_login_history (employee_id, login_ip, login_email, login_type) VALUES ($1, $2, $3, 'GOOGLE') RETURNING history_id `, [employee.employee_id, clientIp, googleEmail]) // 세션 생성 const sessionId = await createSession( employee.employee_id, loginHistory.history_id, clientIp, userAgent ) setSessionCookie(event, sessionId) return sendRedirect(event, '/') } catch (e) { console.error('Google OAuth error:', e) return sendRedirect(event, '/login?error=oauth_failed') } })