codalware-auth
Version:
Complete authentication system with enterprise security, attack protection, team workspaces, waitlist, billing, UI components, 2FA, and account recovery - production-ready in 5 minutes. Enhanced CLI with verification, rollback, and App Router scaffolding.
67 lines (58 loc) • 3.07 kB
text/typescript
import { useCallback, useEffect, useState } from 'react';
interface TwoFactorStatus {
enabled: boolean;
qrCode?: string;
secret?: string;
backupCodes?: string[];
}
export const useTwoFactor = () => {
const [status,setStatus] = useState<TwoFactorStatus>({ enabled: false });
const [loading,setLoading] = useState(false);
const [error,setError] = useState<string|null>(null);
const load = useCallback(async ()=>{
setLoading(true); setError(null);
try {
const res = await fetch('/api/auth/2fa/setup'); // GET for status
if(!res.ok) throw new Error('Failed to load 2FA');
const payload = await res.json();
// API returns { success, data: { enabled, backupCodesCount } }
const enabled = payload?.data?.enabled ?? payload.enabled ?? false;
setStatus(s => ({ ...s, enabled }));
} catch(e: unknown){ setError(e instanceof Error ? e.message : 'Failed to load 2FA'); } finally { setLoading(false); }
},[]);
const beginSetup = async () => {
setLoading(true); setError(null);
try {
const res = await fetch('/api/auth/2fa/setup', { method: 'POST' });
const payload = await res.json();
if(!res.ok || !payload.success) throw new Error(payload.error?.message || 'Setup failed');
// payload.data: { secret, qrCode, backupCodes }
setStatus(prev => ({ ...prev, ...payload.data }));
} catch(e: unknown){ setError(e instanceof Error ? e.message : 'Setup failed'); } finally { setLoading(false); }
};
const verify = async (token: string) => {
setLoading(true); setError(null);
try {
const res = await fetch('/api/auth/2fa/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token }) });
const payload = await res.json();
if(!res.ok || !payload.success) throw new Error(payload.error?.message || 'Verification failed');
setStatus(s => ({ ...s, enabled: true }));
// Refresh session so user object reflects is2FAEnabled
try { await fetch('/api/auth/session?update=1'); } catch {}
return true;
} catch(e: unknown){ setError(e instanceof Error ? e.message : 'Verification failed'); return false; } finally { setLoading(false); }
};
const disable = async (token: string) => {
setLoading(true); setError(null);
try {
const res = await fetch('/api/auth/2fa/disable', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token }) });
const payload = await res.json();
if(!res.ok || !payload.success) throw new Error(payload.error?.message || 'Disable failed');
setStatus({ enabled: false });
try { await fetch('/api/auth/session?update=1'); } catch {}
return true;
} catch(e: unknown){ setError(e instanceof Error ? e.message : 'Disable failed'); return false; } finally { setLoading(false); }
};
useEffect(()=>{ load(); },[load]);
return { status, loading, error, reload: load, beginSetup, verify, disable };
};