summaryrefslogtreecommitdiff
path: root/hooks
diff options
context:
space:
mode:
Diffstat (limited to 'hooks')
-rw-r--r--hooks/use-next-auth-reauth.ts76
-rw-r--r--hooks/use-settings-access.ts118
2 files changed, 194 insertions, 0 deletions
diff --git a/hooks/use-next-auth-reauth.ts b/hooks/use-next-auth-reauth.ts
new file mode 100644
index 00000000..6c17ddae
--- /dev/null
+++ b/hooks/use-next-auth-reauth.ts
@@ -0,0 +1,76 @@
+// hooks/use-next-auth-reauth.ts
+"use client"
+
+import * as React from "react"
+import { useSession } from "next-auth/react"
+
+interface UseNextAuthReAuthOptions {
+ // 재인증 유효 시간 (밀리초, 기본값: 5분)
+ validDuration?: number
+ // 재인증이 필요한지 여부
+ requireReAuth?: boolean
+}
+
+export function useNextAuthReAuth(options: UseNextAuthReAuthOptions = {}) {
+ const { validDuration = 5 * 60 * 1000, requireReAuth = true } = options
+ const { data: session, status, update } = useSession()
+
+ const [showReAuthModal, setShowReAuthModal] = React.useState(false)
+ const [isLoading, setIsLoading] = React.useState(true)
+
+ // 재인증이 필요한지 확인
+ const isAuthenticated = React.useMemo(() => {
+ if (!session || !requireReAuth) {
+ return status === "authenticated"
+ }
+
+ // JWT 토큰에서 재인증 시간 확인
+ const reAuthTime = session.user?.reAuthTime
+ if (!reAuthTime) return false
+
+ const now = Date.now()
+ return (now - reAuthTime) < validDuration
+ }, [session, requireReAuth, validDuration, status])
+
+ React.useEffect(() => {
+ if (status === "loading") return
+
+ if (status === "unauthenticated") {
+ setIsLoading(false)
+ return
+ }
+
+ if (requireReAuth && !isAuthenticated) {
+ setShowReAuthModal(true)
+ }
+
+ setIsLoading(false)
+ }, [status, requireReAuth, isAuthenticated])
+
+ const handleReAuthSuccess = React.useCallback(async () => {
+ // 세션 업데이트 (재인증 시간 포함)
+ await update({
+ reAuthTime: Date.now()
+ })
+ setShowReAuthModal(false)
+ }, [update])
+
+ const forceReAuth = React.useCallback(async () => {
+ // 재인증 강제 실행
+ await update({
+ reAuthTime: null
+ })
+ setShowReAuthModal(true)
+ }, [update])
+
+ return {
+ isAuthenticated,
+ showReAuthModal,
+ isLoading,
+ userEmail: session?.user?.email || "",
+ handleReAuthSuccess,
+ forceReAuth,
+ sessionStatus: status,
+ session,
+ }
+} \ No newline at end of file
diff --git a/hooks/use-settings-access.ts b/hooks/use-settings-access.ts
new file mode 100644
index 00000000..cd7938ac
--- /dev/null
+++ b/hooks/use-settings-access.ts
@@ -0,0 +1,118 @@
+// hooks/use-settings-access.ts (개선된 버전)
+"use client"
+
+import * as React from "react"
+import { useSession } from "next-auth/react"
+import { useRouter } from "next/navigation"
+
+// 인증 방식 타입
+type AuthMethod = 'otp' | 'email' | 'sgips' | 'saml'
+
+interface UseSettingsAccessOptions {
+ // 재인증 유효 시간 (밀리초, 기본값: 5분)
+ validDuration?: number
+ // S-Gips 사용자 리다이렉트 경로
+ sgipsRedirectPath?: string
+}
+
+type AccessType = 'allowed' | 'reauth_required' | 'blocked_sgips' | 'loading' | 'unauthenticated'
+
+interface SettingsAccessState {
+ accessType: AccessType
+ showReAuthModal: boolean
+ isAuthenticated: boolean
+ userEmail: string
+ userId: string
+ userDomain: string | null
+ authMethod: AuthMethod | undefined
+ handleReAuthSuccess: () => Promise<void>
+ forceReAuth: () => void
+}
+
+export function useSettingsAccess(options: UseSettingsAccessOptions = {}): SettingsAccessState {
+ const {
+ validDuration = 5 * 60 * 1000,
+ sgipsRedirectPath = "/dashboard"
+ } = options
+
+ const { data: session, status, update } = useSession()
+ const router = useRouter()
+ const [showReAuthModal, setShowReAuthModal] = React.useState(false)
+
+ // 사용자의 접근 타입 결정 (인증 방식 기반)
+ const accessType: AccessType = React.useMemo(() => {
+ if (status === "loading") return 'loading'
+ if (status === "unauthenticated") return 'unauthenticated'
+ if (!session?.user) return 'unauthenticated'
+
+ const authMethod = session.user.authMethod
+
+ // 인증 방식에 따른 접근 제어
+ switch (authMethod) {
+ case 'sgips':
+ // S-Gips 사용자는 접근 차단
+ return 'blocked_sgips'
+
+ case 'saml':
+ // SAML 사용자는 바로 접근 허용
+ return 'allowed'
+
+ case 'otp':
+ // OTP 사용자는 바로 접근 허용 (이미 2차 인증 완료)
+ return 'allowed'
+
+ case 'email':
+ // 일반 이메일 사용자는 재인증 필요
+ const reAuthTime = session.user.reAuthTime
+ if (!reAuthTime) return 'reauth_required'
+
+ const now = Date.now()
+ const isReAuthValid = (now - reAuthTime) < validDuration
+
+ return isReAuthValid ? 'allowed' : 'reauth_required'
+
+ default:
+ // 인증 방식이 불명확한 경우 재인증 요구
+ console.warn('Unknown auth method:', authMethod)
+ return 'reauth_required'
+ }
+ }, [session, status, validDuration])
+
+ // S-Gips 사용자 자동 리다이렉트
+ React.useEffect(() => {
+ if (accessType === 'blocked_sgips') {
+ router.push(sgipsRedirectPath)
+ }
+ }, [accessType, router, sgipsRedirectPath])
+
+ // 재인증 필요 시 모달 표시
+ React.useEffect(() => {
+ setShowReAuthModal(accessType === 'reauth_required')
+ }, [accessType])
+
+ const handleReAuthSuccess = React.useCallback(async () => {
+ await update({
+ reAuthTime: Date.now()
+ })
+ setShowReAuthModal(false)
+ }, [update])
+
+ const forceReAuth = React.useCallback(async () => {
+ await update({
+ reAuthTime: null
+ })
+ setShowReAuthModal(true)
+ }, [update])
+
+ return {
+ accessType,
+ showReAuthModal,
+ isAuthenticated: accessType === 'allowed',
+ userEmail: session?.user?.email || "",
+ userId: session?.user?.id || "",
+ userDomain: session?.user?.domain || null,
+ authMethod: session?.user?.authMethod,
+ handleReAuthSuccess,
+ forceReAuth,
+ }
+} \ No newline at end of file