feat: add TOTP 2FA, API key switcher, login progress bar, and alpha hardening plan
- TOTP 2FA: full setup/confirm/disable/login flow with Argon2id + ChaCha20-Poly1305 encrypted secret storage, QR code generation, and bcrypt-hashed backup codes - API key switcher: OAuth vs personal API key toggle in AIUI chat settings with status indicator, key validation, and help text - Login progress bar: server startup detection with health check polling, form disabled until server is ready - AI quarantine docs: comprehensive HTML page documenting all 6 security layers - Settings: AI Data Access permission toggles with per-category control - Alpha hardening plan: 28-task overnight automation plan across 7 phases (onboarding, login, app install, AIUI, UI polish, security, ISO build) - Backlog: node discovery spatial map feature for alpha demo Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,23 +27,28 @@ export const useAppStore = defineStore('app', () => {
|
||||
const isOffline = computed(() => !isConnected.value || isRestarting.value || isShuttingDown.value)
|
||||
|
||||
// Actions
|
||||
async function login(password: string): Promise<void> {
|
||||
async function login(password: string): Promise<{ requires_totp?: boolean }> {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await rpcClient.login(password)
|
||||
const result = await rpcClient.login(password)
|
||||
if (result && result.requires_totp) {
|
||||
return { requires_totp: true }
|
||||
}
|
||||
|
||||
isAuthenticated.value = true
|
||||
sessionValidated = true
|
||||
localStorage.setItem('neode-auth', 'true')
|
||||
|
||||
|
||||
// Initialize data structure immediately so dashboard can render
|
||||
await initializeData()
|
||||
|
||||
|
||||
// Connect WebSocket in background - don't block login flow
|
||||
connectWebSocket().catch((err) => {
|
||||
console.warn('[Store] WebSocket connection failed after login, will retry:', err)
|
||||
})
|
||||
return {}
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : 'Login failed'
|
||||
throw err
|
||||
@@ -52,6 +57,16 @@ export const useAppStore = defineStore('app', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function completeLoginAfterTotp(): Promise<void> {
|
||||
isAuthenticated.value = true
|
||||
sessionValidated = true
|
||||
localStorage.setItem('neode-auth', 'true')
|
||||
await initializeData()
|
||||
connectWebSocket().catch((err) => {
|
||||
console.warn('[Store] WebSocket connection failed after TOTP login, will retry:', err)
|
||||
})
|
||||
}
|
||||
|
||||
async function logout(): Promise<void> {
|
||||
try {
|
||||
await rpcClient.logout()
|
||||
@@ -285,6 +300,7 @@ export const useAppStore = defineStore('app', () => {
|
||||
|
||||
// Actions
|
||||
login,
|
||||
completeLoginAfterTotp,
|
||||
logout,
|
||||
checkSession,
|
||||
needsSessionValidation,
|
||||
|
||||
Reference in New Issue
Block a user