diff --git a/frontend/auth/synology/callback.vue b/frontend/auth/synology/callback.vue
index 5c3c659..1046f1b 100644
--- a/frontend/auth/synology/callback.vue
+++ b/frontend/auth/synology/callback.vue
@@ -17,7 +17,6 @@ onMounted(async () => {
const hash = window.location.hash.substring(1)
const params = new URLSearchParams(hash)
const accessToken = params.get('access_token')
- const state = params.get('state')
const error = params.get('error')
if (error) {
@@ -32,11 +31,16 @@ onMounted(async () => {
try {
// 서버로 access_token 전송하여 로그인 처리
- await $fetch('/api/auth/synology/verify', {
+ const result = await $fetch<{ success: boolean; needPasswordSet?: boolean }>('/api/auth/synology/verify', {
method: 'POST',
- body: { accessToken, state }
+ body: { accessToken }
})
- router.push('/')
+
+ if (result.needPasswordSet) {
+ router.push('/set-password?from=synology')
+ } else {
+ router.push('/')
+ }
} catch (e: any) {
router.push(`/login?error=${encodeURIComponent(e.data?.message || 'Synology 로그인에 실패했습니다.')}`)
}
diff --git a/frontend/login.vue b/frontend/login.vue
index 0f4add7..cf7de9c 100644
--- a/frontend/login.vue
+++ b/frontend/login.vue
@@ -36,9 +36,9 @@
Google로 로그인
-
+
- Synology로 로그인
+ 오솔NAS로 로그인
diff --git a/server/api/auth/login.post.ts b/server/api/auth/login.post.ts
deleted file mode 100644
index 60f8e0d..0000000
--- a/server/api/auth/login.post.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import { getClientIp } from '../../utils/ip'
-import { createSession, setSessionCookie } from '../../utils/session'
-
-interface LoginBody {
- email: string
- name: string
-}
-
-/**
- * 이메일+이름 로그인
- * POST /api/auth/login
- */
-export default defineEventHandler(async (event) => {
- const body = await readBody(event)
- const clientIp = getClientIp(event)
- const userAgent = getHeader(event, 'user-agent') || null
-
- if (!body.email || !body.name) {
- throw createError({ statusCode: 400, message: '이메일과 이름을 입력해주세요.' })
- }
-
- // 이메일 형식 검증
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
- if (!emailRegex.test(body.email)) {
- throw createError({ statusCode: 400, message: '올바른 이메일 형식이 아닙니다.' })
- }
-
- const emailLower = body.email.toLowerCase()
- const nameTrimmed = body.name.trim()
-
- // 기존 직원 조회
- let employee = await query(`
- SELECT * FROM wr_employee_info WHERE employee_email = $1
- `, [emailLower])
-
- let employeeData = employee[0]
-
- if (employeeData) {
- // 기존 직원 - 이름이 다르면 업데이트
- if (employeeData.employee_name !== nameTrimmed) {
- await execute(`
- UPDATE wr_employee_info
- SET employee_name = $1, updated_at = NOW(), updated_ip = $2, updated_email = $3
- WHERE employee_id = $4
- `, [nameTrimmed, clientIp, emailLower, employeeData.employee_id])
- employeeData.employee_name = nameTrimmed
- }
- } else {
- // 신규 직원 자동 등록
- employeeData = await insertReturning(`
- INSERT INTO wr_employee_info (employee_name, employee_email, created_ip, created_email, updated_ip, updated_email)
- VALUES ($1, $2, $3, $2, $3, $2)
- RETURNING *
- `, [nameTrimmed, emailLower, clientIp])
- }
-
- // 로그인 이력 추가
- const loginHistory = await insertReturning(`
- INSERT INTO wr_login_history (employee_id, login_ip, login_email)
- VALUES ($1, $2, $3)
- RETURNING history_id
- `, [employeeData.employee_id, clientIp, emailLower])
-
- // DB 기반 세션 생성
- const sessionId = await createSession(
- employeeData.employee_id,
- loginHistory.history_id,
- clientIp,
- userAgent
- )
-
- // 세션 쿠키 설정
- setSessionCookie(event, sessionId)
-
- return {
- success: true,
- user: {
- employeeId: employeeData.employee_id,
- employeeName: employeeData.employee_name,
- employeeEmail: employeeData.employee_email,
- employeePosition: employeeData.employee_position
- }
- }
-})
diff --git a/server/api/auth/recent-users.get.ts b/server/api/auth/recent-users.get.ts
deleted file mode 100644
index 83900fb..0000000
--- a/server/api/auth/recent-users.get.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { query } from '../../utils/db'
-
-/**
- * 최근 로그인 사용자 목록
- * GET /api/auth/recent-users
- */
-export default defineEventHandler(async () => {
- const users = await query(`
- SELECT * FROM wr_recent_login_users
- ORDER BY last_active_at DESC
- LIMIT 10
- `)
-
- return {
- users: users.map((u: any) => ({
- employeeId: u.employee_id,
- employeeName: u.employee_name,
- employeeEmail: u.employee_email,
- employeePosition: u.employee_position,
- lastActiveAt: u.last_active_at
- }))
- }
-})
diff --git a/server/api/auth/select-user.post.ts b/server/api/auth/select-user.post.ts
deleted file mode 100644
index d564073..0000000
--- a/server/api/auth/select-user.post.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { getClientIp } from '../../utils/ip'
-import { createSession, setSessionCookie } from '../../utils/session'
-
-interface SelectUserBody {
- employeeId: number
-}
-
-/**
- * 기존 사용자 선택 로그인
- * POST /api/auth/select-user
- */
-export default defineEventHandler(async (event) => {
- const body = await readBody(event)
- const clientIp = getClientIp(event)
- const userAgent = getHeader(event, 'user-agent') || null
-
- if (!body.employeeId) {
- throw createError({ statusCode: 400, message: '사용자를 선택해주세요.' })
- }
-
- // 사원 조회
- const employee = await queryOne(`
- SELECT * FROM wr_employee_info
- WHERE employee_id = $1 AND is_active = true
- `, [body.employeeId])
-
- if (!employee) {
- throw createError({ statusCode: 404, message: '사용자를 찾을 수 없습니다.' })
- }
-
- // 로그인 이력 추가
- const loginHistory = await insertReturning(`
- INSERT INTO wr_login_history (employee_id, login_ip, login_email)
- VALUES ($1, $2, $3)
- RETURNING history_id
- `, [employee.employee_id, clientIp, employee.employee_email])
-
- // DB 기반 세션 생성
- const sessionId = await createSession(
- employee.employee_id,
- loginHistory.history_id,
- clientIp,
- userAgent
- )
-
- // 세션 쿠키 설정
- setSessionCookie(event, sessionId)
-
- return {
- success: true,
- user: {
- employeeId: employee.employee_id,
- employeeName: employee.employee_name,
- employeeEmail: employee.employee_email,
- employeePosition: employee.employee_position
- }
- }
-})
diff --git a/server/api/auth/synology/verify.post.ts b/server/api/auth/synology/verify.post.ts
index de9b7c3..ae23e50 100644
--- a/server/api/auth/synology/verify.post.ts
+++ b/server/api/auth/synology/verify.post.ts
@@ -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(userInfoUrl)
+ console.log('[Synology SSO] Requesting user info...')
- if (!userInfoResponse.success) {
- throw createError({
- statusCode: 401,
- message: userInfoResponse.error?.msg || 'Synology 토큰 검증에 실패했습니다.'
- })
+ let userResponse = await $fetch(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(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
+ }
}
- // 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(`
+ 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 로그인 처리 중 오류가 발생했습니다.'