feat: dashboard setup with passkey authentication

Scaffold Vite + React + TypeScript project with Tailwind CSS dark theme.
Add Axios API client with JWT interceptor and auto-refresh, WebAuthn
passkey auth flow (register/login), protected route wrapper, and React
Router with public and protected routes.
This commit is contained in:
Viktor Barzin 2026-02-22 15:54:32 +00:00
parent f218865872
commit f121f376ae
No known key found for this signature in database
GPG key ID: 0EB088298288D958
20 changed files with 5274 additions and 0 deletions

View file

@ -0,0 +1,75 @@
import { useState, useCallback, useMemo } from 'react';
import { startRegistration, startAuthentication } from '@simplewebauthn/browser';
import {
registerBegin,
registerComplete,
loginBegin,
loginComplete,
} from '../api/auth';
export interface UseAuthReturn {
isAuthenticated: boolean;
login: (username: string) => Promise<void>;
register: (username: string) => Promise<void>;
logout: () => void;
error: string | null;
loading: boolean;
}
export function useAuth(): UseAuthReturn {
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const [token, setToken] = useState<string | null>(() =>
localStorage.getItem('access_token')
);
const isAuthenticated = useMemo(() => !!token, [token]);
const register = useCallback(async (username: string) => {
setLoading(true);
setError(null);
try {
const { options } = await registerBegin(username);
const attestation = await startRegistration({ optionsJSON: options as any });
const tokens = await registerComplete(username, attestation);
localStorage.setItem('access_token', tokens.access_token);
localStorage.setItem('refresh_token', tokens.refresh_token);
setToken(tokens.access_token);
} catch (err: any) {
const message =
err?.response?.data?.detail || err?.message || 'Registration failed';
setError(message);
throw err;
} finally {
setLoading(false);
}
}, []);
const login = useCallback(async (username: string) => {
setLoading(true);
setError(null);
try {
const { options } = await loginBegin(username);
const assertion = await startAuthentication({ optionsJSON: options as any });
const tokens = await loginComplete(username, assertion);
localStorage.setItem('access_token', tokens.access_token);
localStorage.setItem('refresh_token', tokens.refresh_token);
setToken(tokens.access_token);
} catch (err: any) {
const message =
err?.response?.data?.detail || err?.message || 'Login failed';
setError(message);
throw err;
} finally {
setLoading(false);
}
}, []);
const logout = useCallback(() => {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
setToken(null);
}, []);
return { isAuthenticated, login, register, logout, error, loading };
}