diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 321eb9f..1b1b932 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -1,7 +1,14 @@
diff --git a/frontend/src/stores/auth.ts b/frontend/src/stores/auth.ts
index 63a03c3..4f8020f 100644
--- a/frontend/src/stores/auth.ts
+++ b/frontend/src/stores/auth.ts
@@ -58,6 +58,27 @@ export const useAuthStore = defineStore('auth', () => {
permissions.value = {}
}
+ /**
+ * Validate the persisted session against the API on app boot. Without this,
+ * a stale/revoked token renders the full panel chrome and only collapses on
+ * the first real API call. useApi's 401 path (refresh → retry → logout)
+ * does the heavy lifting; any non-auth failure (network, 5xx) keeps the
+ * session — never log users out because the API blipped.
+ * Dynamic import avoids a static auth-store ↔ useApi module cycle.
+ */
+ async function validateSession(): Promise {
+ if (!accessToken.value) return
+ try {
+ const { useApi } = await import('@/composables/useApi')
+ const me = await useApi().get>('/auth/me')
+ if (user.value && me && typeof me === 'object') {
+ user.value = { ...user.value, ...me }
+ }
+ } catch {
+ // 401 → refresh → logout/redirect already handled inside useApi.
+ }
+ }
+
function hasModule(moduleSlug: string): boolean {
return license.value?.modules_enabled?.includes(moduleSlug) ?? false
}
@@ -92,6 +113,7 @@ export const useAuthStore = defineStore('auth', () => {
setAuth,
setLicense,
logout,
+ validateSession,
hasModule,
hasPermission,
}