import { queryOne, execute } from '../../../utils/db' import { createSession } from '../../../utils/session' import { getClientIp } from '../../../utils/ip' /** * Synology SSO 콜백 * GET /api/auth/synology/callback * * Synology SSO Server에서 인증 후 리다이렉트되는 엔드포인트 */ export default defineEventHandler(async (event) => { const config = useRuntimeConfig() const query = getQuery(event) const ip = getClientIp(event) const code = query.code as string const error = query.error as string if (error) { return sendRedirect(event, `/login?error=${encodeURIComponent('Synology 인증이 취소되었습니다.')}`) } if (!code) { return sendRedirect(event, '/login?error=' + encodeURIComponent('인증 코드가 없습니다.')) } try { // 1. 코드로 액세스 토큰 교환 const tokenResponse = await $fetch(`${config.synologyServerUrl}/webman/sso/SSOAccessToken.cgi`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code: code, client_id: config.synologyClientId, client_secret: config.synologyClientSecret, redirect_uri: config.synologyRedirectUri }).toString() }) if (!tokenResponse.access_token) { console.error('Synology token error:', tokenResponse) return sendRedirect(event, '/login?error=' + encodeURIComponent('토큰 획득 실패')) } // 2. 액세스 토큰으로 사용자 정보 조회 const userResponse = await $fetch(`${config.synologyServerUrl}/webman/sso/SSOUserInfo.cgi`, { method: 'GET', headers: { 'Authorization': `Bearer ${tokenResponse.access_token}` } }) if (!userResponse.data || !userResponse.data.email) { console.error('Synology user info error:', userResponse) return sendRedirect(event, '/login?error=' + encodeURIComponent('사용자 정보를 가져올 수 없습니다.')) } const synologyEmail = userResponse.data.email const synologyId = userResponse.data.user_id || userResponse.data.uid const synologyName = userResponse.data.name || userResponse.data.username // 3. 이메일로 사용자 매칭 const employee = await queryOne(` SELECT employee_id, employee_name, is_active, password_hash, synology_id, synology_email FROM wr_employee_info WHERE email = $1 `, [synologyEmail]) if (!employee) { return sendRedirect(event, '/login?error=' + encodeURIComponent('등록되지 않은 사용자입니다. 관리자에게 문의하세요.')) } if (!employee.is_active) { return sendRedirect(event, '/login?error=' + encodeURIComponent('비활성화된 계정입니다.')) } // 4. Synology 계정 연결 정보 업데이트 await execute(` UPDATE wr_employee_info SET synology_id = $1, synology_email = $2, synology_linked_at = NOW() WHERE employee_id = $3 `, [synologyId, synologyEmail, employee.employee_id]) // 5. 로그인 이력 기록 await execute(` INSERT INTO wr_login_history (employee_id, login_type, login_ip, login_at, login_success, login_email) VALUES ($1, 'SYNOLOGY', $2, NOW(), true, $3) `, [employee.employee_id, ip, synologyEmail]) // 6. 세션 생성 await createSession(event, employee.employee_id) // 7. 비밀번호 미설정 시 설정 페이지로 if (!employee.password_hash) { return sendRedirect(event, '/set-password?from=synology') } // 8. 메인 페이지로 리다이렉트 return sendRedirect(event, '/') } catch (e: any) { console.error('Synology OAuth error:', e) return sendRedirect(event, '/login?error=' + encodeURIComponent('Synology 인증 중 오류가 발생했습니다.')) } })