From 8ace0480ae831c76c11383a6f8a9aef097391218 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Tue, 14 Apr 2026 16:24:58 +0200 Subject: [PATCH] fix: handle 401 gracefully in auth initialization after httpOnly migration Race condition: the axios 401 interceptor uses a dynamic import, so handleUnauthorized() fires AFTER doInitialize() sets isInitialized=true. handleUnauthorized() then reset isInitialized to false, leaving the app stuck on a loading spinner with no way to recover. Fix: remove isInitialized=false from handleUnauthorized() in all three apps. When handleUnauthorized() redirects via window.location.href, all JS state resets naturally. When it skips the redirect (already on a public page like /login), the app should render normally in an unauthenticated state. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/admin/src/stores/useAuthStore.ts | 5 ++++- apps/app/src/stores/useAuthStore.ts | 5 ++++- apps/portal/src/stores/useAuthStore.ts | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/admin/src/stores/useAuthStore.ts b/apps/admin/src/stores/useAuthStore.ts index 7b319496..a6b1cf94 100644 --- a/apps/admin/src/stores/useAuthStore.ts +++ b/apps/admin/src/stores/useAuthStore.ts @@ -43,7 +43,10 @@ export const useAuthStore = defineStore('auth', () => { function handleUnauthorized() { clearState() - isInitialized.value = false + // Do NOT reset isInitialized — the full page reload (below) resets all JS state. + // Resetting it here causes a race condition: the async 401 interceptor fires + // after doInitialize() sets isInitialized=true, putting the app back into + // a loading state that never resolves. if (typeof window !== 'undefined') { const publicPaths = ['/login', '/forgot-password', '/reset-password', '/verify-email-change'] diff --git a/apps/app/src/stores/useAuthStore.ts b/apps/app/src/stores/useAuthStore.ts index 78bb1af2..151f8869 100644 --- a/apps/app/src/stores/useAuthStore.ts +++ b/apps/app/src/stores/useAuthStore.ts @@ -60,7 +60,10 @@ export const useAuthStore = defineStore('auth', () => { function handleUnauthorized() { clearState() - isInitialized.value = false + // Do NOT reset isInitialized — the full page reload (below) resets all JS state. + // Resetting it here causes a race condition: the async 401 interceptor fires + // after doInitialize() sets isInitialized=true, putting the app back into + // a loading state that never resolves. if (typeof window !== 'undefined' && window.location.pathname !== '/login') { window.location.href = '/login' diff --git a/apps/portal/src/stores/useAuthStore.ts b/apps/portal/src/stores/useAuthStore.ts index 4287b97f..e659716c 100644 --- a/apps/portal/src/stores/useAuthStore.ts +++ b/apps/portal/src/stores/useAuthStore.ts @@ -25,7 +25,10 @@ export const useAuthStore = defineStore('auth', () => { function handleUnauthorized() { clearState() - isInitialized.value = false + // Do NOT reset isInitialized — the full page reload (below) resets all JS state. + // Resetting it here causes a race condition: the async 401 interceptor fires + // after doInitialize() sets isInitialized=true, putting the app back into + // a loading state that never resolves. if (typeof window !== 'undefined') { const path = window.location.pathname