mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-26 07:04:38 +02:00
feat(auth): add TOTP two-factor authentication across all apps
Uses Better Auth's built-in twoFactor plugin for TOTP + backup codes: Backend (mana-core-auth): - twoFactor plugin in better-auth.config.ts (issuer: ManaCore) - twoFactorEnabled field on users table, backupCodes as encrypted text - 2FA redirect detection in signIn flow - Passthrough controller forwards /two-factor/* to Better Auth - Security event types for 2FA operations Client (shared-auth): - enableTwoFactor, disableTwoFactor, verifyTwoFactor, verifyBackupCode, generateBackupCodes methods with session-to-token exchange UI (shared-auth-ui): - LoginPage: 2FA code input view after password login, backup code toggle - TwoFactorSetup: settings component with enable/disable/QR code/backup codes App integration: - All 19 auth stores have verifyTwoFactor() and verifyBackupCode() - All 19 login pages pass onVerifyTwoFactor and onVerifyBackupCode callbacks - ManaCore settings page has TwoFactorSetup component Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
90e6135637
commit
f5a9edcfb6
49 changed files with 1800 additions and 169 deletions
|
|
@ -122,6 +122,29 @@ export const authStore = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -122,6 +122,29 @@ export const authStore = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -105,6 +105,28 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -122,6 +122,29 @@ export const authStore = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -122,6 +122,29 @@ export const authStore = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -106,6 +106,28 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -138,6 +138,46 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async enableTwoFactor(password: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available' };
|
||||
return authService.enableTwoFactor(password);
|
||||
},
|
||||
|
||||
async disableTwoFactor(password: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available' };
|
||||
return authService.disableTwoFactor(password);
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async generateBackupCodes(password: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available' };
|
||||
return authService.generateBackupCodes(password);
|
||||
},
|
||||
|
||||
async registerPasskey(friendlyName?: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available' };
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { Button, Input, Card, PageHeader, GlobalSettingsSection } from '@manacore/shared-ui';
|
||||
import { PasskeyManager } from '@manacore/shared-auth-ui';
|
||||
import { PasskeyManager, TwoFactorSetup } from '@manacore/shared-auth-ui';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { creditsService } from '$lib/api/credits';
|
||||
import type { CreditBalance } from '$lib/api/credits';
|
||||
|
|
@ -294,6 +294,19 @@
|
|||
</div>
|
||||
</Card>
|
||||
|
||||
<!-- Two-Factor Authentication Section -->
|
||||
<Card>
|
||||
<div class="p-6">
|
||||
<TwoFactorSetup
|
||||
enabled={!!authStore.user?.twoFactorEnabled}
|
||||
onEnable={(password) => authStore.enableTwoFactor(password)}
|
||||
onDisable={(password) => authStore.disableTwoFactor(password)}
|
||||
onGenerateBackupCodes={(password) => authStore.generateBackupCodes(password)}
|
||||
primaryColor="#6366f1"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<!-- My Data & Danger Zone -->
|
||||
<Card>
|
||||
<div class="p-6">
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect="/dashboard"
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -93,6 +93,24 @@ export const authStore = {
|
|||
return true;
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = toManaUser(userData);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = toManaUser(userData);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
return authService.isPasskeyAvailable();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect="/decks"
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -107,6 +107,28 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -120,6 +120,29 @@ export const authStore = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -109,6 +109,28 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -120,6 +120,29 @@ export const authStore = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect="/app/gallery"
|
||||
registerPath="/auth/signup"
|
||||
|
|
|
|||
|
|
@ -115,6 +115,28 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -84,6 +84,28 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -119,6 +119,29 @@ export const auth = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={auth.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => auth.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => auth.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => auth.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -113,6 +113,28 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -117,6 +117,28 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -118,6 +118,29 @@ export const authStore = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -133,6 +133,29 @@ export const authStore = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
|
|
@ -122,6 +122,29 @@ export const authStore = {
|
|||
/**
|
||||
* Check if passkeys are available in this browser
|
||||
*/
|
||||
|
||||
async verifyTwoFactor(code: string, trustDevice?: boolean) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyTwoFactor(code, trustDevice);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
async verifyBackupCode(code: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return { success: false, error: 'Auth not available on server' };
|
||||
const result = await authService.verifyBackupCode(code);
|
||||
if (result.success) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = userData;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
isPasskeyAvailable(): boolean {
|
||||
const authService = getAuthService();
|
||||
if (!authService) return false;
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@
|
|||
onResendVerification={handleResendVerification}
|
||||
passkeyAvailable={authStore.isPasskeyAvailable()}
|
||||
onSignInWithPasskey={() => authStore.signInWithPasskey()}
|
||||
onVerifyTwoFactor={(code, trust) => authStore.verifyTwoFactor(code, trust)}
|
||||
onVerifyBackupCode={(code) => authStore.verifyBackupCode(code)}
|
||||
{goto}
|
||||
successRedirect={redirectTo}
|
||||
registerPath="/register"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue