'use client'; import { FormEvent, useState } from 'react'; import { signInWithPopup } from 'firebase/auth'; import { firebaseAuth, googleProvider } from '../lib/firebase-client'; type AuthMode = 'login' | 'register'; export function AuthForm({ mode }: { mode: AuthMode }) { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [deviceName, setDeviceName] = useState('Web browser'); const [message, setMessage] = useState(''); const [googleCanceled, setGoogleCanceled] = useState(false); const [busy, setBusy] = useState(false); const [googleBusy, setGoogleBusy] = useState(false); async function handleGoogleSignIn() { setGoogleBusy(true); setMessage(''); setGoogleCanceled(false); try { const credential = await signInWithPopup(firebaseAuth, googleProvider); const idToken = await credential.user.getIdToken(); // Exchange firebase idToken with backend to set server session & ops cookies let response: Response | undefined; try { response = await fetch('/api/auth/firebase-login', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id_token: idToken }), }); } catch { // Fallback to legacy link if available const legacyResponse = await fetch('/api/account/firebase-link', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id_token: idToken }), }); if (!legacyResponse.ok) { throw new Error('Unable to establish a secure session. Please try again.'); } } if (response && !response.ok) { const body = await response.json().catch(() => ({})); throw new Error(body.detail ?? 'Unable to establish a secure session. Please try again.'); } const destination = new URLSearchParams(window.location.search).get('returnTo') || '/account'; window.location.assign(destination); } catch (error) { const code = error && typeof error === 'object' && 'code' in error ? error.code : undefined; if (code === 'auth/popup-closed-by-user' || code === 'auth/cancelled-popup-request') { setGoogleCanceled(true); setMessage('Google sign-in was canceled. You can try again.'); return; } console.error('Google Sign-In Error:', error); setMessage(error instanceof Error ? error.message : 'Google sign-in failed. Please try again.'); } finally { setGoogleBusy(false); } } async function submit(event: FormEvent) { event.preventDefault(); setBusy(true); setMessage(''); setGoogleCanceled(false); try { const response = await fetch(`/api/auth/${mode}`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email, password, device_name: deviceName }), }); const body = await response.json().catch(() => ({})); if (!response.ok) throw new Error(body.detail ?? 'Authentication failed.'); const destination = new URLSearchParams(window.location.search).get('returnTo') || '/account'; window.location.assign(destination); } catch (error) { setMessage(error instanceof Error ? error.message : 'Authentication failed.'); } finally { setBusy(false); } } return (
{/* Google Sign-In / Registration Button */} {/* Divider */}
or continue with email
{/* Email / Password Form */}
setEmail(e.target.value)} className="w-full px-3.5 py-2.5 rounded-xl bg-surface-container-low border border-outline-variant/40 focus:border-primary-container focus:ring-1 focus:ring-primary-container text-white text-sm outline-none transition placeholder:text-slate-500" />
setPassword(e.target.value)} className="w-full px-3.5 py-2.5 rounded-xl bg-surface-container-low border border-outline-variant/40 focus:border-primary-container focus:ring-1 focus:ring-primary-container text-white text-sm outline-none transition placeholder:text-slate-500" />
{mode === 'register' && (
setDeviceName(e.target.value)} className="w-full px-3.5 py-2.5 rounded-xl bg-surface-container-low border border-outline-variant/40 focus:border-primary-container focus:ring-1 focus:ring-primary-container text-white text-sm outline-none transition placeholder:text-slate-500" />
)} {message && (
{message}
)}
); }