+
{
refreshTimer = null
}
- // Calculate time until refresh (refresh 10 minutes before expiry for better safety margin)
- const refreshBuffer = 10 * 60 * 1000 // 10 minutes in milliseconds (increased from 5)
+ // Calculate time until refresh (refresh 5 minutes before expiry)
+ const refreshBuffer = 5 * 60 * 1000 // 5 minutes in milliseconds
const timeUntilRefresh = expiresAt - Date.now() - refreshBuffer
console.log('[AUTH_REFRESH] Scheduling token refresh in:', Math.max(0, timeUntilRefresh), 'ms')
@@ -165,15 +165,43 @@ export default defineNuxtPlugin(() => {
if (typeof document !== 'undefined') {
let lastVisibilityChange = Date.now()
- document.addEventListener('visibilitychange', () => {
+ document.addEventListener('visibilitychange', async () => {
if (!document.hidden) {
const now = Date.now()
const timeSinceLastCheck = now - lastVisibilityChange
- // If tab was hidden for more than 1 minute, check auth status
- if (timeSinceLastCheck > 60000) {
+ // If tab was hidden for more than 30 seconds, check auth status
+ if (timeSinceLastCheck > 30000) {
console.log('[AUTH_REFRESH] Tab became visible after', Math.round(timeSinceLastCheck / 1000), 'seconds, checking auth status')
- checkAndScheduleRefresh()
+
+ // Force immediate session validation
+ try {
+ const response = await fetch('/api/auth/session', {
+ headers: {
+ 'Cache-Control': 'no-cache',
+ 'Pragma': 'no-cache'
+ }
+ })
+
+ if (!response.ok || response.status === 401) {
+ console.log('[AUTH_REFRESH] Session expired while tab was hidden')
+ await navigateTo('/login')
+ return
+ }
+
+ const sessionData = await response.json()
+ if (!sessionData.authenticated) {
+ console.log('[AUTH_REFRESH] Not authenticated after tab visibility')
+ await navigateTo('/login')
+ return
+ }
+
+ // Re-schedule refresh if session is valid
+ checkAndScheduleRefresh()
+ } catch (error) {
+ console.error('[AUTH_REFRESH] Failed to check session on visibility change:', error)
+ await navigateTo('/login')
+ }
}
lastVisibilityChange = now
@@ -181,11 +209,41 @@ export default defineNuxtPlugin(() => {
})
}
- // Clean up timer on plugin destruction
+ // Add periodic session validation (every 2 minutes)
+ let validationInterval: NodeJS.Timeout | null = null
+
+ onMounted(() => {
+ validationInterval = setInterval(async () => {
+ console.log('[AUTH_REFRESH] Performing periodic session validation')
+
+ try {
+ const response = await fetch('/api/auth/session', {
+ headers: {
+ 'Cache-Control': 'no-cache',
+ 'Pragma': 'no-cache'
+ }
+ })
+
+ if (!response.ok || response.status === 401) {
+ console.log('[AUTH_REFRESH] Session invalid during periodic check')
+ clearInterval(validationInterval!)
+ await navigateTo('/login')
+ }
+ } catch (error) {
+ console.error('[AUTH_REFRESH] Periodic validation error:', error)
+ }
+ }, 2 * 60 * 1000) // Every 2 minutes
+ })
+
+ // Clean up timers on plugin destruction
onBeforeUnmount(() => {
if (refreshTimer) {
clearTimeout(refreshTimer)
refreshTimer = null
}
+ if (validationInterval) {
+ clearInterval(validationInterval)
+ validationInterval = null
+ }
})
})
diff --git a/plugins/02.auth-error-handler.client.ts b/plugins/02.auth-error-handler.client.ts
new file mode 100644
index 0000000..68ade63
--- /dev/null
+++ b/plugins/02.auth-error-handler.client.ts
@@ -0,0 +1,119 @@
+export default defineNuxtPlugin(() => {
+ // Only run on client side
+ if (import.meta.server) return
+
+ const nuxtApp = useNuxtApp()
+ const toast = useToast()
+
+ // Global error handler for API requests
+ nuxtApp.hook('app:error', (error: any) => {
+ console.error('[AUTH_ERROR_HANDLER] Application error:', error)
+
+ // Handle authentication errors
+ if (error.statusCode === 401 || error.statusCode === 403) {
+ handleAuthError(error)
+ }
+ })
+
+ // Intercept $fetch errors globally
+ const originalFetch = globalThis.$fetch
+ globalThis.$fetch = $fetch.create({
+ onResponseError({ response }) {
+ console.log('[AUTH_ERROR_HANDLER] Response error:', {
+ status: response.status,
+ url: response.url,
+ statusText: response.statusText
+ })
+
+ // Handle authentication errors (401, 403)
+ if (response.status === 401 || response.status === 403) {
+ handleAuthError({
+ statusCode: response.status,
+ statusMessage: response.statusText,
+ data: response._data
+ })
+ }
+
+ // Handle 404 errors that might be auth-related
+ if (response.status === 404 && isProtectedRoute()) {
+ console.warn('[AUTH_ERROR_HANDLER] 404 on protected route, may be auth-related')
+ // Check if session is still valid
+ checkAndHandleSession()
+ }
+ }
+ })
+
+ const handleAuthError = async (error: any) => {
+ console.error('[AUTH_ERROR_HANDLER] Authentication error detected:', error)
+
+ // Clear all auth-related caches
+ clearAuthCaches()
+
+ // Only show toast and redirect if we're not already on the login page
+ const route = useRoute()
+ if (route.path !== '/login' && !route.path.startsWith('/auth')) {
+ toast.error('Your session has expired. Please log in again.')
+
+ // Delay navigation slightly to ensure toast is visible
+ setTimeout(() => {
+ navigateTo('/login')
+ }, 500)
+ }
+ }
+
+ const clearAuthCaches = () => {
+ console.log('[AUTH_ERROR_HANDLER] Clearing authentication caches')
+
+ // Clear Nuxt app payload caches
+ if (nuxtApp.payload.data) {
+ delete nuxtApp.payload.data['auth:session:cache']
+ delete nuxtApp.payload.data.authState
+ }
+
+ // Clear session cookie
+ const sessionCookie = useCookie('nuxt-oidc-auth')
+ sessionCookie.value = null
+ }
+
+ const isProtectedRoute = () => {
+ const route = useRoute()
+ // Check if current route requires authentication
+ return route.meta.auth !== false &&
+ !route.path.startsWith('/login') &&
+ !route.path.startsWith('/auth')
+ }
+
+ const checkAndHandleSession = async () => {
+ try {
+ // Force a fresh session check without cache
+ const response = await fetch('/api/auth/session', {
+ headers: {
+ 'Cache-Control': 'no-cache',
+ 'Pragma': 'no-cache'
+ }
+ })
+
+ if (!response.ok) {
+ throw new Error(`Session check failed: ${response.status}`)
+ }
+
+ const sessionData = await response.json()
+
+ if (!sessionData.authenticated) {
+ handleAuthError({
+ statusCode: 401,
+ statusMessage: 'Session expired'
+ })
+ }
+ } catch (error) {
+ console.error('[AUTH_ERROR_HANDLER] Failed to check session:', error)
+ handleAuthError({
+ statusCode: 401,
+ statusMessage: 'Session check failed'
+ })
+ }
+ }
+
+ // Expose clearAuthCaches for manual use
+ nuxtApp.provide('clearAuthCaches', clearAuthCaches)
+})
diff --git a/remove.txt b/remove.txt
new file mode 100644
index 0000000..8fc5278
--- /dev/null
+++ b/remove.txt
@@ -0,0 +1,2 @@
+mkdir: cannot create directory ‘C:\\Users\\mpcia\\Documents\\Cline\\MCP’: File exists
+hello
\ No newline at end of file