fix: Synology SSO Implicit Grant 방식으로 변경
- response_type: code → token - redirect_uri: /api/... → /auth/... (프론트엔드 페이지) - 프론트엔드 callback 페이지 추가 (hash fragment 파싱) - verify API 추가 (access_token 검증)
This commit is contained in:
2
.env
2
.env
@@ -20,7 +20,7 @@ OPENAI_API_KEY=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
|
|||||||
SYNOLOGY_SERVER_URL=https://sso.osolit.net
|
SYNOLOGY_SERVER_URL=https://sso.osolit.net
|
||||||
SYNOLOGY_CLIENT_ID=dd477ab92304fa1cb1a2e1c6b96a6331
|
SYNOLOGY_CLIENT_ID=dd477ab92304fa1cb1a2e1c6b96a6331
|
||||||
SYNOLOGY_CLIENT_SECRET=
|
SYNOLOGY_CLIENT_SECRET=
|
||||||
SYNOLOGY_REDIRECT_URI=http://127.0.0.1/api/auth/synology/callback
|
SYNOLOGY_REDIRECT_URI=http://127.0.0.1/auth/synology/callback
|
||||||
|
|
||||||
# EMAIL (SMTP)
|
# EMAIL (SMTP)
|
||||||
SMTP_HOST=smtp.gmail.com
|
SMTP_HOST=smtp.gmail.com
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ OPENAI_API_KEY=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
|
|||||||
SYNOLOGY_SERVER_URL=https://sso.osolit.net
|
SYNOLOGY_SERVER_URL=https://sso.osolit.net
|
||||||
SYNOLOGY_CLIENT_ID=afa90b7b4a9773af41f2c7b110f92d6e
|
SYNOLOGY_CLIENT_ID=afa90b7b4a9773af41f2c7b110f92d6e
|
||||||
SYNOLOGY_CLIENT_SECRET=
|
SYNOLOGY_CLIENT_SECRET=
|
||||||
SYNOLOGY_REDIRECT_URI=https://weeklyreport.turbosoft.kr/api/auth/synology/callback
|
SYNOLOGY_REDIRECT_URI=https://weeklyreport.turbosoft.kr/auth/synology/callback
|
||||||
|
|
||||||
# EMAIL (SMTP)
|
# EMAIL (SMTP)
|
||||||
SMTP_HOST=smtp.gmail.com
|
SMTP_HOST=smtp.gmail.com
|
||||||
|
|||||||
44
frontend/auth/synology/callback.vue
Normal file
44
frontend/auth/synology/callback.vue
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<div class="d-flex justify-content-center align-items-center min-vh-100">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="spinner-border text-primary mb-3" role="status"></div>
|
||||||
|
<p class="text-muted">Synology 로그인 처리 중...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({ layout: false })
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// hash fragment에서 access_token 추출
|
||||||
|
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) {
|
||||||
|
router.push(`/login?error=${encodeURIComponent('Synology 인증이 취소되었습니다.')}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accessToken) {
|
||||||
|
router.push(`/login?error=${encodeURIComponent('인증 토큰이 없습니다.')}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 서버로 access_token 전송하여 로그인 처리
|
||||||
|
await $fetch('/api/auth/synology/verify', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { accessToken, state }
|
||||||
|
})
|
||||||
|
router.push('/')
|
||||||
|
} catch (e: any) {
|
||||||
|
router.push(`/login?error=${encodeURIComponent(e.data?.message || 'Synology 로그인에 실패했습니다.')}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -65,7 +65,7 @@ export default defineNuxtConfig({
|
|||||||
synologyServerUrl: process.env.SYNOLOGY_SERVER_URL || 'https://sso.osolit.net',
|
synologyServerUrl: process.env.SYNOLOGY_SERVER_URL || 'https://sso.osolit.net',
|
||||||
synologyClientId: process.env.SYNOLOGY_CLIENT_ID || 'afa90b7b4a9773af41f2c7b110f92d6e',
|
synologyClientId: process.env.SYNOLOGY_CLIENT_ID || 'afa90b7b4a9773af41f2c7b110f92d6e',
|
||||||
synologyClientSecret: process.env.SYNOLOGY_CLIENT_SECRET || '',
|
synologyClientSecret: process.env.SYNOLOGY_CLIENT_SECRET || '',
|
||||||
synologyRedirectUri: process.env.SYNOLOGY_REDIRECT_URI || 'https://weeklyreport.turbosoft.kr/api/auth/synology/callback',
|
synologyRedirectUri: process.env.SYNOLOGY_REDIRECT_URI || 'https://weeklyreport.turbosoft.kr/auth/synology/callback',
|
||||||
public: {
|
public: {
|
||||||
appName: '주간업무보고'
|
appName: '주간업무보고'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
// Synology SSO Server OAuth 인증 URL
|
// Synology SSO Server OAuth 인증 URL
|
||||||
const authUrl = new URL(`${config.synologyServerUrl}/webman/sso/SSOOauth.cgi`)
|
const authUrl = new URL(`${config.synologyServerUrl}/webman/sso/SSOOauth.cgi`)
|
||||||
authUrl.searchParams.set('response_type', 'code')
|
authUrl.searchParams.set('response_type', 'token')
|
||||||
authUrl.searchParams.set('app_id', config.synologyClientId)
|
authUrl.searchParams.set('app_id', config.synologyClientId)
|
||||||
authUrl.searchParams.set('redirect_uri', config.synologyRedirectUri)
|
authUrl.searchParams.set('redirect_uri', config.synologyRedirectUri)
|
||||||
authUrl.searchParams.set('scope', 'user_id')
|
authUrl.searchParams.set('scope', 'user_id')
|
||||||
|
|||||||
90
server/api/auth/synology/verify.post.ts
Normal file
90
server/api/auth/synology/verify.post.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* Synology SSO Access Token 검증 및 로그인 처리
|
||||||
|
* POST /api/auth/synology/verify
|
||||||
|
*/
|
||||||
|
import { sql } from '~/server/utils/db'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody(event)
|
||||||
|
const { accessToken } = body
|
||||||
|
|
||||||
|
if (!accessToken) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
message: 'Access token이 없습니다.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Synology SSO Server에서 사용자 정보 조회
|
||||||
|
const userInfoUrl = `${config.synologyServerUrl}/webman/sso/SSOAccessToken.cgi?action=exchange&access_token=${accessToken}&app_id=${config.synologyClientId}`
|
||||||
|
|
||||||
|
const userInfoResponse = await $fetch<any>(userInfoUrl)
|
||||||
|
|
||||||
|
if (!userInfoResponse.success) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 401,
|
||||||
|
message: userInfoResponse.error?.msg || 'Synology 토큰 검증에 실패했습니다.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const synologyUsername = userInfoResponse.data?.user_name || userInfoResponse.data?.user_id
|
||||||
|
|
||||||
|
if (!synologyUsername) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 401,
|
||||||
|
message: 'Synology 사용자 정보를 가져올 수 없습니다.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DB에서 synology_username으로 사용자 조회
|
||||||
|
const users = await sql`
|
||||||
|
SELECT employee_id, employee_name, employee_email, employee_role, company_id
|
||||||
|
FROM employees
|
||||||
|
WHERE synology_username = ${synologyUsername}
|
||||||
|
AND employee_status = 'active'
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 404,
|
||||||
|
message: `Synology 계정 "${synologyUsername}"과 연결된 사용자를 찾을 수 없습니다. 관리자에게 문의하세요.`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = users[0]
|
||||||
|
|
||||||
|
// 세션 생성
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
user: {
|
||||||
|
id: user.employee_id,
|
||||||
|
name: user.employee_name,
|
||||||
|
email: user.employee_email,
|
||||||
|
role: user.employee_role
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.statusCode) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
console.error('Synology SSO verify error:', error)
|
||||||
|
throw createError({
|
||||||
|
statusCode: 500,
|
||||||
|
message: 'Synology 로그인 처리 중 오류가 발생했습니다.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user