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

@@ -11,6 +11,8 @@ export const useAuthStore = defineStore('auth', () => {
const permissions = ref<string[]>([])
const isInitialized = ref(false)
const mfaSetupRequired = ref(false)
const isAuthenticated = computed(() => !!user.value)
const isSuperAdmin = computed(() => appRoles.value?.includes('super_admin') ?? false)
@@ -35,6 +37,7 @@ export const useAuthStore = defineStore('auth', () => {
organisations.value = me.organisations
appRoles.value = me.app_roles
permissions.value = me.permissions
mfaSetupRequired.value = me.mfa?.setup_required ?? false
// Auto-select first organisation if none is active
const orgStore = useOrganisationStore()
@@ -53,6 +56,7 @@ export const useAuthStore = defineStore('auth', () => {
organisations.value = []
appRoles.value = []
permissions.value = []
mfaSetupRequired.value = false
const orgStore = useOrganisationStore()
orgStore.clear()
@@ -120,6 +124,7 @@ export const useAuthStore = defineStore('auth', () => {
isInitialized,
isSuperAdmin,
currentOrganisation,
mfaSetupRequired,
setUser,
setActiveOrganisation,
logout,