fix: MFA setup completion not updating UI state
Root cause: the MFA status endpoint returned `mfa_enabled` as the JSON key but the TypeScript MfaStatus interface expected `enabled`. At runtime, `mfaStatus.value?.enabled` was always `undefined`, so `isEnabled` was always false — the banner never hid and the method cards never showed "Geconfigureerd". Additionally, the auth store had no way to re-fetch /auth/me after initialization, so `mfaSetupRequired` was never properly refreshed from the backend after MFA setup. Fixes: - Rename `mfa_enabled` → `enabled` in the MFA status endpoint response to match the TypeScript type (and the /auth/me MeResource which already used `enabled`) - Add `refreshUser()` to the auth store for post-initialization re-fetching of /auth/me - Call `refreshUser()` in onSetupCompleted so the store reflects the backend state without a full page reload - Update backend tests to match the renamed response key Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -135,7 +135,7 @@ final class MfaSetupController extends Controller
|
||||
$user = $request->user();
|
||||
|
||||
return $this->success([
|
||||
'mfa_enabled' => $user->mfa_enabled,
|
||||
'enabled' => $user->mfa_enabled,
|
||||
'method' => $user->mfa_method,
|
||||
'confirmed_at' => $user->mfa_confirmed_at?->toIso8601String(),
|
||||
'backup_codes_remaining' => $this->mfaService->getRemainingBackupCodeCount($user),
|
||||
|
||||
@@ -147,7 +147,7 @@ class MfaSetupControllerTest extends TestCase
|
||||
$response->assertOk()
|
||||
->assertJson([
|
||||
'data' => [
|
||||
'mfa_enabled' => false,
|
||||
'enabled' => false,
|
||||
'method' => null,
|
||||
'confirmed_at' => null,
|
||||
'backup_codes_remaining' => 0,
|
||||
@@ -164,7 +164,7 @@ class MfaSetupControllerTest extends TestCase
|
||||
$response->assertOk()
|
||||
->assertJson([
|
||||
'data' => [
|
||||
'mfa_enabled' => true,
|
||||
'enabled' => true,
|
||||
'method' => 'totp',
|
||||
'backup_codes_remaining' => 10,
|
||||
],
|
||||
|
||||
@@ -80,10 +80,13 @@ const backupCodesColor = computed(() => {
|
||||
})
|
||||
|
||||
function onSetupCompleted() {
|
||||
// Refetch MFA status so the method cards update (enabled, method, backup codes)
|
||||
refetchMfaStatus()
|
||||
// Immediately clear the MFA enforcement state so the banner disappears
|
||||
// and the router guard no longer redirects
|
||||
// Immediately clear the enforcement flag so the router guard unblocks navigation
|
||||
authStore.mfaSetupRequired = false
|
||||
// Refresh the full /auth/me response into the store so the flag stays
|
||||
// correct on subsequent navigations (without needing a full page reload)
|
||||
authStore.refreshUser()
|
||||
}
|
||||
|
||||
function onDisabled() {
|
||||
|
||||
@@ -88,6 +88,20 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-fetch /auth/me and update the store. Safe to call at any time
|
||||
* after initialization (e.g. after MFA setup or profile update).
|
||||
*/
|
||||
async function refreshUser(): Promise<void> {
|
||||
try {
|
||||
const { data } = await apiClient.get<{ success: boolean; data: MeResponse }>('/auth/me')
|
||||
setUser(data.data)
|
||||
}
|
||||
catch {
|
||||
// Silently ignore — the existing state stays
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once on app startup. Validates the httpOnly cookie by calling
|
||||
* GET /auth/me. On 401, clears everything.
|
||||
@@ -132,5 +146,6 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
logout,
|
||||
handleUnauthorized,
|
||||
initialize,
|
||||
refreshUser,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user