108 lines
3.9 KiB
TypeScript
108 lines
3.9 KiB
TypeScript
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<any>(`${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<any>(`${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<any>(`
|
|
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 인증 중 오류가 발생했습니다.'))
|
|
}
|
|
})
|