feat(frontend): validate persisted session on app boot
A stale or revoked token previously rendered the full panel chrome and only collapsed on the first API call. App boot now calls /auth/me through useApi (401 -> refresh -> logout already handled there); user profile refreshes on success, and non-auth failures (network, 5xx) never log the user out. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import { RouterView } from 'vue-router'
|
||||
import ToastNotification from '@/components/ToastNotification.vue'
|
||||
import ErrorBoundary from '@/components/ErrorBoundary.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
// Validate any persisted session against the API on boot — a stale token
|
||||
// should bounce to login immediately, not after the first failed call.
|
||||
const auth = useAuthStore()
|
||||
onMounted(() => { void auth.validateSession() })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -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<void> {
|
||||
if (!accessToken.value) return
|
||||
try {
|
||||
const { useApi } = await import('@/composables/useApi')
|
||||
const me = await useApi().get<Partial<User>>('/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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user