mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-24 00:36:42 +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
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue