feat: add google oauth
This commit is contained in:
81
src/pages/AuthCallbackPage.tsx
Normal file
81
src/pages/AuthCallbackPage.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { useAuth, OAUTH_POST_LOGIN_REDIRECT_KEY } from '../contexts/AuthContext';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
function toInternalPath(urlOrPath: string): string {
|
||||
try {
|
||||
const parsed = new URL(urlOrPath, window.location.origin);
|
||||
if (parsed.origin !== window.location.origin) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
||||
} catch {
|
||||
return '/';
|
||||
}
|
||||
}
|
||||
|
||||
export default function AuthCallbackPage() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const { completeGoogleOAuth } = useAuth();
|
||||
const { t } = useLanguage();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const code = useMemo(() => searchParams.get('code') ?? '', [searchParams]);
|
||||
const state = useMemo(() => searchParams.get('state') ?? '', [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
const run = async () => {
|
||||
if (!code || !state) {
|
||||
if (mounted) {
|
||||
setError(t.auth.oauthFailed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const redirectUri = `${window.location.origin}/auth/google/callback`;
|
||||
const result = await completeGoogleOAuth({ code, state, redirect_uri: redirectUri });
|
||||
|
||||
if (result.error) {
|
||||
if (mounted) {
|
||||
setError(result.error.message || t.auth.oauthFailed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const redirectTarget = sessionStorage.getItem(OAUTH_POST_LOGIN_REDIRECT_KEY) || '/';
|
||||
navigate(toInternalPath(redirectTarget), { replace: true });
|
||||
};
|
||||
|
||||
run();
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, [code, completeGoogleOAuth, navigate, state, t.auth.oauthFailed]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl w-full max-w-md p-6 text-center">
|
||||
<h1 className="text-xl font-bold text-gray-900 mb-3">Google OAuth</h1>
|
||||
{!error && <p className="text-gray-600">{t.auth.oauthExchanging}</p>}
|
||||
{error && (
|
||||
<>
|
||||
<p className="text-red-600 text-sm mb-4">{error}</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/', { replace: true })}
|
||||
className="px-4 py-2 bg-gray-900 text-white rounded-lg hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
Back Home
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user