import { useEffect, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router'; import { useTranslation } from 'react-i18next'; import { useAuthStore } from '../store/auth'; // SessionStorage helpers for OAuth state const OAUTH_STATE_KEY = 'oauth_state'; const OAUTH_PROVIDER_KEY = 'oauth_provider'; export function saveOAuthState(state: string, provider: string): void { sessionStorage.setItem(OAUTH_STATE_KEY, state); sessionStorage.setItem(OAUTH_PROVIDER_KEY, provider); } export function getAndClearOAuthState(): { state: string; provider: string } | null { const state = sessionStorage.getItem(OAUTH_STATE_KEY); const provider = sessionStorage.getItem(OAUTH_PROVIDER_KEY); sessionStorage.removeItem(OAUTH_STATE_KEY); sessionStorage.removeItem(OAUTH_PROVIDER_KEY); if (!state || !provider) return null; return { state, provider }; } export default function OAuthCallback() { const { t } = useTranslation(); const navigate = useNavigate(); const [searchParams] = useSearchParams(); const [error, setError] = useState(''); const { loginWithOAuth, isAuthenticated } = useAuthStore(); useEffect(() => { if (isAuthenticated) { navigate('/', { replace: true }); return; } const authenticate = async () => { const code = searchParams.get('code'); const urlState = searchParams.get('state'); if (!code || !urlState) { setError(t('auth.oauthError', 'Authorization was denied or failed')); return; } // Get saved state from sessionStorage const saved = getAndClearOAuthState(); if (!saved) { setError(t('auth.oauthExpired', 'OAuth session expired. Please try again.')); return; } // Validate state match if (saved.state !== urlState) { setError(t('auth.oauthError', 'Authorization was denied or failed')); return; } try { await loginWithOAuth(saved.provider, code, urlState); navigate('/', { replace: true }); } catch (err: unknown) { const error = err as { response?: { data?: { detail?: string } } }; setError( error.response?.data?.detail || t('auth.oauthError', 'Authorization was denied or failed'), ); } }; authenticate(); }, [searchParams, loginWithOAuth, navigate, isAuthenticated, t]); if (error) { return (
{error}
{t('common.loading')}