기능구현중

This commit is contained in:
2026-01-11 14:32:45 +09:00
parent 56fc4c3005
commit 0205e8d437
17 changed files with 1015 additions and 70 deletions

View File

@@ -1,9 +1,13 @@
/**
* Google OAuth 시작
* GET /api/auth/google
*
* Query params:
* - extend: 'groups' - 구글 그룹 접근 권한 추가 요청
*/
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig()
const query = getQuery(event)
const clientId = config.googleClientId || process.env.GOOGLE_CLIENT_ID
const redirectUri = config.googleRedirectUri || process.env.GOOGLE_REDIRECT_URI || 'http://localhost:3000/api/auth/google/callback'
@@ -12,7 +16,18 @@ export default defineEventHandler(async (event) => {
throw createError({ statusCode: 500, message: 'Google OAuth가 설정되지 않았습니다.' })
}
const scope = encodeURIComponent('openid email profile')
// 기본 scope + 확장 scope
let scopes = ['openid', 'email', 'profile']
// 구글 그룹 권한 요청 시 추가 scope
if (query.extend === 'groups') {
scopes.push(
'https://www.googleapis.com/auth/gmail.readonly', // 그룹 메일 읽기
'https://www.googleapis.com/auth/cloud-identity.groups.readonly' // 그룹 정보 읽기
)
}
const scope = encodeURIComponent(scopes.join(' '))
const state = Math.random().toString(36).substring(7) // CSRF 방지
// state를 쿠키에 저장

View File

@@ -35,7 +35,9 @@ export default defineEventHandler(async (event) => {
updated_ip,
password_hash,
google_id,
google_email
google_email,
synology_id,
synology_email
FROM wr_employee_info
WHERE employee_id = $1
`, [session.employeeId])
@@ -60,7 +62,9 @@ export default defineEventHandler(async (event) => {
updatedIp: employee.updated_ip,
hasPassword: !!employee.password_hash,
googleId: employee.google_id,
googleEmail: employee.google_email
googleEmail: employee.google_email,
synologyId: employee.synology_id,
synologyEmail: employee.synology_email
}
}
})

View File

@@ -0,0 +1,107 @@
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 인증 중 오류가 발생했습니다.'))
}
})

View File

@@ -0,0 +1,26 @@
/**
* Synology SSO 로그인 시작
* GET /api/auth/synology
*
* Synology SSO Server OAuth 2.0 인증 페이지로 리다이렉트
*/
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig()
if (!config.synologyServerUrl || !config.synologyClientId) {
throw createError({
statusCode: 500,
message: 'Synology SSO가 설정되지 않았습니다.'
})
}
// Synology SSO Server OAuth 인증 URL
const authUrl = new URL(`${config.synologyServerUrl}/webman/sso/SSOOauth.cgi`)
authUrl.searchParams.set('response_type', 'code')
authUrl.searchParams.set('client_id', config.synologyClientId)
authUrl.searchParams.set('redirect_uri', config.synologyRedirectUri)
authUrl.searchParams.set('scope', 'user_id')
authUrl.searchParams.set('state', crypto.randomUUID())
return sendRedirect(event, authUrl.toString())
})