import { useState, useEffect, useCallback } from 'react';
import { useT } from '../i18n/LanguageContext';
import { useAccount, useSignMessage } from 'wagmi';
import { SiweContext } from './siweAuthContext';
/**
* Wallet-based session auth (SIWE). Backend: /api/auth/{nonce,login,me,logout}.
* Session = httpOnly cookie, so all fetches use credentials:'include'. The
* connected wallet signs a server-issued nonce message (personal_sign).
*/
export function SiweAuthProvider({ children }: { children: React.ReactNode }) {
const { t } = useT();
const { address, isConnected } = useAccount();
const { signMessageAsync } = useSignMessage();
const [user, setUser] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// Restore session on mount (cookie may already be valid).
useEffect(() => {
const t = setTimeout(async () => {
try {
const r = await fetch('/api/auth/me', { credentials: 'include' });
if (r.ok) {
const d = await r.json();
if (d.success) setUser(d.address);
}
} catch { /* ignore */ }
}, 0);
return () => clearTimeout(t);
}, []);
const login = useCallback(async () => {
if (!isConnected || !address) {
setError(t('err.connectFirst'));
return;
}
setLoading(true);
setError(null);
try {
const nr = await fetch(`/api/auth/nonce?address=${address}`, { credentials: 'include' });
const nd = await nr.json();
if (!nd.success) throw new Error(nd.error || 'nonce failed');
const signature = await signMessageAsync({ message: nd.message });
const lr = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'content-type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ address, signature }),
});
const ld = await lr.json();
if (!lr.ok || !ld.success) throw new Error(ld.error || 'login failed');
setUser(ld.address);
} catch (e) {
setError(e instanceof Error ? e.message : t('err.signinFailed'));
} finally {
setLoading(false);
}
}, [isConnected, address, signMessageAsync, t]);
const logout = useCallback(async () => {
try {
await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' });
} catch { /* ignore */ }
setUser(null);
}, []);
return (
<SiweContext.Provider value={{ user, isConnected, address, loading, error, login, logout }}>
{children}
</SiweContext.Provider>
);
}
📜 Git History
6c47fa4chore: local Polikopi project home + Phase 1 redesign artifacts12 days ago
Show last diff
Loading...