wing-ops/frontend/src/store/authStore.ts
htlee 7743e40767 feat(auth): Google OAuth 로그인 연동
- google-auth-library로 Google ID Token 검증 (backend)
- @react-oauth/google GoogleLogin 컴포넌트 (frontend)
- gcsc.co.kr 도메인 자동 승인(ACTIVE), 기타 도메인 PENDING
- 기존 ID/PW 사용자와 OAuth 사용자 동일 계정 체계 통합
- AdminView: 사용자 인증방식(Google/ID PW) 뱃지 표시
- AdminView: OAuth 자동 승인 도메인 설정 UI
- deploy.yml: VITE_GOOGLE_CLIENT_ID 빌드 환경변수 추가
- nginx: Cross-Origin-Opener-Policy 헤더 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:42:59 +09:00

75 lines
2.1 KiB
TypeScript

import { create } from 'zustand'
import { loginApi, googleLoginApi, logoutApi, fetchMe } from '../services/authApi'
import type { AuthUser } from '../services/authApi'
interface AuthState {
user: AuthUser | null
isAuthenticated: boolean
isLoading: boolean
error: string | null
login: (account: string, password: string) => Promise<void>
googleLogin: (credential: string) => Promise<void>
logout: () => Promise<void>
checkSession: () => Promise<void>
hasPermission: (resource: string) => boolean
clearError: () => void
}
export const useAuthStore = create<AuthState>((set, get) => ({
user: null,
isAuthenticated: false,
isLoading: true,
error: null,
login: async (account: string, password: string) => {
set({ isLoading: true, error: null })
try {
const user = await loginApi(account, password)
set({ user, isAuthenticated: true, isLoading: false })
} catch (err) {
const message = (err as { message?: string })?.message || '로그인에 실패했습니다.'
set({ isLoading: false, error: message })
throw err
}
},
googleLogin: async (credential: string) => {
set({ isLoading: true, error: null })
try {
const user = await googleLoginApi(credential)
set({ user, isAuthenticated: true, isLoading: false })
} catch (err) {
const message = (err as { message?: string })?.message || 'Google 로그인에 실패했습니다.'
set({ isLoading: false, error: message })
throw err
}
},
logout: async () => {
try {
await logoutApi()
} catch {
// 로그아웃 실패해도 클라이언트 상태는 초기화
}
set({ user: null, isAuthenticated: false, isLoading: false, error: null })
},
checkSession: async () => {
set({ isLoading: true })
try {
const user = await fetchMe()
set({ user, isAuthenticated: true, isLoading: false })
} catch {
set({ user: null, isAuthenticated: false, isLoading: false })
}
},
hasPermission: (resource: string) => {
const { user } = get()
if (!user) return false
return user.permissions.includes(resource)
},
clearError: () => set({ error: null }),
}))