130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
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
|
|
})
|
|
|
|
// Only handle authentication errors from our own API endpoints
|
|
const isAuthEndpoint = response.url && (
|
|
response.url.includes('/api/auth/') ||
|
|
response.url.includes('/api/') && !response.url.includes('cms.portnimara.dev') && !response.url.includes('database.portnimara.com')
|
|
)
|
|
|
|
// Handle authentication errors (401, 403) only from our API
|
|
if ((response.status === 401 || response.status === 403) && isAuthEndpoint) {
|
|
console.log('[AUTH_ERROR_HANDLER] Authentication error from app endpoint')
|
|
handleAuthError({
|
|
statusCode: response.status,
|
|
statusMessage: response.statusText,
|
|
data: response._data
|
|
})
|
|
} else if (response.status === 401 && !isAuthEndpoint) {
|
|
console.log('[AUTH_ERROR_HANDLER] Ignoring 401 from external service:', response.url)
|
|
// Don't clear auth for external service 401s
|
|
}
|
|
|
|
// Handle 404 errors that might be auth-related
|
|
if (response.status === 404 && isProtectedRoute() && isAuthEndpoint) {
|
|
console.warn('[AUTH_ERROR_HANDLER] 404 on protected route from app endpoint, 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)
|
|
})
|