/** * Synology SSO Access Token 검증 및 로그인 처리 * POST /api/auth/synology/verify */ import { queryOne, execute } from '~/server/utils/db' import { createSession } from '~/server/utils/session' import { getClientIp } from '~/server/utils/ip' export default defineEventHandler(async (event) => { const body = await readBody(event) const { accessToken } = body const ip = getClientIp(event) if (!accessToken) { throw createError({ statusCode: 400, message: 'Access token이 없습니다.' }) } const config = useRuntimeConfig() console.log('[Synology SSO] Access Token:', accessToken) try { // 1. 액세스 토큰으로 사용자 정보 조회 const userInfoUrl = `${config.synologyServerUrl}/webman/sso/SSOUserInfo.cgi` console.log('[Synology SSO] Requesting user info...') let userResponse = await $fetch(userInfoUrl, { method: 'GET', headers: { 'Authorization': `Bearer ${accessToken}` } }) // 문자열로 온 경우 JSON 파싱 if (typeof userResponse === 'string') { userResponse = JSON.parse(userResponse) } console.log('[Synology SSO] User Response:', JSON.stringify(userResponse, null, 2)) if (!userResponse.data || !userResponse.data.email) { // email이 없으면 SSOAccessToken.cgi로 시도 const tokenInfoUrl = `${config.synologyServerUrl}/webman/sso/SSOAccessToken.cgi?action=exchange&access_token=${accessToken}&app_id=${config.synologyClientId}` let tokenResponse = await $fetch(tokenInfoUrl) if (typeof tokenResponse === 'string') { tokenResponse = JSON.parse(tokenResponse) } console.log('[Synology SSO] Token Response:', JSON.stringify(tokenResponse, null, 2)) if (!tokenResponse.success) { throw createError({ statusCode: 401, message: 'Synology 사용자 정보를 가져올 수 없습니다.' }) } // user_name으로 사용자 조회 const synologyUsername = tokenResponse.data?.user_name if (!synologyUsername) { throw createError({ statusCode: 401, message: 'Synology 사용자명을 가져올 수 없습니다.' }) } console.log('[Synology SSO] Username:', synologyUsername) // synology_id나 email의 앞부분으로 매칭 시도 const synologyEmail = `${synologyUsername}@osolit.net` const employee = await queryOne(` SELECT employee_id, employee_name, is_active, password_hash, synology_id, synology_email, employee_email FROM wr_employee_info WHERE synology_id = $1 OR employee_email = $2 `, [synologyUsername, synologyEmail]) if (!employee) { throw createError({ statusCode: 404, message: `Synology 계정 "${synologyUsername}"과 연결된 사용자를 찾을 수 없습니다. 관리자에게 문의하세요.` }) } if (!employee.is_active) { throw createError({ statusCode: 403, message: '비활성화된 계정입니다.' }) } // 로그인 이력 기록 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, synologyUsername]) // 세션 생성 await createSession(event, employee.employee_id) return { success: true, needPasswordSet: !employee.password_hash } } // 이메일이 있는 경우 const synologyEmail = userResponse.data.email const synologyId = userResponse.data.user_id || userResponse.data.uid console.log('[Synology SSO] Email:', synologyEmail) // 이메일로 사용자 매칭 const employee = await queryOne(` SELECT employee_id, employee_name, is_active, password_hash, synology_id, synology_email FROM wr_employee_info WHERE employee_email = $1 `, [synologyEmail]) if (!employee) { throw createError({ statusCode: 404, message: '등록되지 않은 사용자입니다. 관리자에게 문의하세요.' }) } if (!employee.is_active) { throw createError({ statusCode: 403, message: '비활성화된 계정입니다.' }) } // 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]) // 로그인 이력 기록 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]) // 세션 생성 await createSession(event, employee.employee_id) return { success: true, needPasswordSet: !employee.password_hash } } catch (error: any) { console.error('[Synology SSO] Error:', error) if (error.statusCode) { throw error } throw createError({ statusCode: 500, message: 'Synology 로그인 처리 중 오류가 발생했습니다.' }) } })