diff --git a/README.md b/README.md index 1e21d59..ce08a6d 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,13 @@ change them. The flip side of that build-time capture is a deployment contract: those policy variables, rebuild the app, or the auth pages will keep advertising the old capabilities (the server still enforces its own policy either way). +Every account can enable TOTP two-factor authentication from the signed-in user menu. Setup at +`/two-factor` requires the account password, displays a QR code plus one-time backup codes, and +does not become active until the first authenticator code verifies. The same route handles the +second-factor challenge after password sign-in, including backup-code recovery and an optional +30-day trusted-device cookie. Apply the checked-in database migrations before deploying this +feature: the Better Auth plugin adds `user.two_factor_enabled` and the `two_factor` table. + The interface ships English and Italian through `@nuxtjs/i18n`, with dictionaries split by scope in `packages/i18n/locales//` and shared with the marketing site; each app loads only the scopes it renders. `aube run i18n:status` builds a diff --git a/apps/dashboard/app/auth.config.ts b/apps/dashboard/app/auth.config.ts index 5446f9d..355a01c 100644 --- a/apps/dashboard/app/auth.config.ts +++ b/apps/dashboard/app/auth.config.ts @@ -6,6 +6,7 @@ import { lastLoginMethodClient, multiSessionClient, organizationClient, + twoFactorClient, } from 'better-auth/client/plugins'; // Better Auth is mounted in this app's own server (`server/auth.config.ts`), so every request is @@ -39,6 +40,11 @@ export default defineClientAuth(() => { betterEnrollmentClient(), lastLoginMethodClient(), multiSessionClient(), + twoFactorClient({ + onTwoFactorRedirect: async () => { + await navigateTo('/two-factor'); + }, + }), deviceAuthorizationClient(), dashClient(), // The one entry that is gated, and the only one whose absence changes nothing about the diff --git a/apps/dashboard/app/pages/two-factor.vue b/apps/dashboard/app/pages/two-factor.vue new file mode 100644 index 0000000..2804971 --- /dev/null +++ b/apps/dashboard/app/pages/two-factor.vue @@ -0,0 +1,269 @@ + + + diff --git a/apps/dashboard/modules/auth/components/TwoFactorError.vue b/apps/dashboard/modules/auth/components/TwoFactorError.vue new file mode 100644 index 0000000..7577a33 --- /dev/null +++ b/apps/dashboard/modules/auth/components/TwoFactorError.vue @@ -0,0 +1,13 @@ + + + diff --git a/apps/dashboard/modules/auth/components/UserMenu.vue b/apps/dashboard/modules/auth/components/UserMenu.vue index 1479c85..e635a5e 100644 --- a/apps/dashboard/modules/auth/components/UserMenu.vue +++ b/apps/dashboard/modules/auth/components/UserMenu.vue @@ -6,6 +6,9 @@

{{ user?.name || user?.email }}

+ + {{ $t('auth.twoFactor.link') }} +