refactor: Synology SSO 개선 및 불필요한 로그인 API 정리

- Synology 버튼: '오솔NAS로 로그인'으로 변경
- 이메일 매칭: {username}@osolit.net으로 매칭
- 삭제된 API:
  - login.post.ts (이메일+이름 로그인)
  - recent-users.get.ts (최근 로그인 사용자)
  - select-user.post.ts (사용자 선택 로그인)
This commit is contained in:
2026-01-11 23:42:50 +09:00
parent 77262bc33a
commit fad322c218
6 changed files with 136 additions and 213 deletions

View File

@@ -2,11 +2,14 @@
* Synology SSO Access Token 검증 및 로그인 처리
* POST /api/auth/synology/verify
*/
import { query } from '~/server/utils/db'
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({
@@ -17,71 +20,152 @@ export default defineEventHandler(async (event) => {
const config = useRuntimeConfig()
console.log('[Synology SSO] Access Token:', accessToken)
try {
// Synology SSO Server에서 사용자 정보 조회
const userInfoUrl = `${config.synologyServerUrl}/webman/sso/SSOAccessToken.cgi?action=exchange&access_token=${accessToken}&app_id=${config.synologyClientId}`
// 1. 액세스 토큰으로 사용자 정보 조회
const userInfoUrl = `${config.synologyServerUrl}/webman/sso/SSOUserInfo.cgi`
const userInfoResponse = await $fetch<any>(userInfoUrl)
console.log('[Synology SSO] Requesting user info...')
if (!userInfoResponse.success) {
throw createError({
statusCode: 401,
message: userInfoResponse.error?.msg || 'Synology 토큰 검증에 실패했습니다.'
})
let userResponse = await $fetch<any>(userInfoUrl, {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`
}
})
// 문자열로 온 경우 JSON 파싱
if (typeof userResponse === 'string') {
userResponse = JSON.parse(userResponse)
}
const synologyUsername = userInfoResponse.data?.user_name || userInfoResponse.data?.user_id
console.log('[Synology SSO] User Response:', JSON.stringify(userResponse, null, 2))
if (!synologyUsername) {
throw createError({
statusCode: 401,
message: 'Synology 사용자 정보를 가져올 수 없습니다.'
})
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<any>(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<any>(`
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
}
}
// DB에서 synology_username으로 사용자 조회
const users = await query(`
SELECT employee_id, employee_name, employee_email, employee_role, company_id
FROM employees
WHERE synology_username = $1
AND employee_status = 'active'
LIMIT 1
`, [synologyUsername])
// 이메일이 있는 경우
const synologyEmail = userResponse.data.email
const synologyId = userResponse.data.user_id || userResponse.data.uid
if (users.length === 0) {
console.log('[Synology SSO] Email:', synologyEmail)
// 이메일로 사용자 매칭
const employee = await queryOne<any>(`
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: `Synology 계정 "${synologyUsername}"과 연결된 사용자를 찾을 수 없습니다. 관리자에게 문의하세요.`
message: '등록되지 않은 사용자입니다. 관리자에게 문의하세요.'
})
}
const user = users[0]
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])
// 세션 생성
const session = await useAuthSession(event)
await session.update({
userId: user.employee_id,
userName: user.employee_name,
userEmail: user.employee_email,
userRole: user.employee_role,
companyId: user.company_id,
loginType: 'synology'
})
await createSession(event, employee.employee_id)
return {
success: true,
user: {
id: user.employee_id,
name: user.employee_name,
email: user.employee_email,
role: user.employee_role
}
needPasswordSet: !employee.password_hash
}
} catch (error: any) {
console.error('[Synology SSO] Error:', error)
if (error.statusCode) {
throw error
}
console.error('Synology SSO verify error:', error)
throw createError({
statusCode: 500,
message: 'Synology 로그인 처리 중 오류가 발생했습니다.'