feat: MFA frontend with auth page restyling, challenge screen, and setup wizard

- Restyle organizer auth pages: Dutch text, remove placeholder social login
- Restyle portal auth pages to Vuexy v1 centered card pattern with decorative shapes
- MFA challenge card component with VOtpInput, method tabs, backup code input,
  trusted device checkbox, and session countdown timer
- Login pages handle mfa_required response with device fingerprint header
- Security settings page with TOTP setup (QR code), email setup, disable MFA,
  backup codes regeneration, and trusted devices management
- Portal profile page includes MFA security section
- Admin user detail page shows MFA status with reset button
- MFA enforcement route guard redirects to security settings when required
- Device fingerprint utility for trusted device identification
- MFA types, composables with TanStack Query for both apps

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-15 21:32:17 +02:00
parent a9e8e9bb62
commit 0be2956ea4
38 changed files with 3991 additions and 377 deletions

View File

@@ -52,9 +52,11 @@ declare global {
const extendRef: typeof import('@vueuse/core')['extendRef']
const formatDate: typeof import('./src/@core/utils/formatters')['formatDate']
const formatDateToMonthShort: typeof import('./src/@core/utils/formatters')['formatDateToMonthShort']
const generateDeviceFingerprint: typeof import('./src/utils/deviceFingerprint')['generateDeviceFingerprint']
const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const getDeviceName: typeof import('./src/utils/deviceFingerprint')['getDeviceName']
const h: typeof import('vue')['h']
const hexToRgb: typeof import('./src/@core/utils/colorConverter')['hexToRgb']
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
@@ -421,9 +423,11 @@ declare module 'vue' {
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
readonly formatDate: UnwrapRef<typeof import('./src/@core/utils/formatters')['formatDate']>
readonly formatDateToMonthShort: UnwrapRef<typeof import('./src/@core/utils/formatters')['formatDateToMonthShort']>
readonly generateDeviceFingerprint: UnwrapRef<typeof import('./src/utils/deviceFingerprint')['generateDeviceFingerprint']>
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getDeviceName: UnwrapRef<typeof import('./src/utils/deviceFingerprint')['getDeviceName']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly hexToRgb: UnwrapRef<typeof import('./src/@core/utils/colorConverter')['hexToRgb']>
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>