기능구현중
This commit is contained in:
@@ -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를 쿠키에 저장
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
107
backend/api/auth/synology/callback.get.ts
Normal file
107
backend/api/auth/synology/callback.get.ts
Normal 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 인증 중 오류가 발생했습니다.'))
|
||||
}
|
||||
})
|
||||
26
backend/api/auth/synology/index.get.ts
Normal file
26
backend/api/auth/synology/index.get.ts
Normal 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())
|
||||
})
|
||||
Reference in New Issue
Block a user