55 lines
1.7 KiB
TypeScript
55 lines
1.7 KiB
TypeScript
/**
|
|
* 🌐 Global API Plugin — 401 Unauthorized Interceptor
|
|
*
|
|
* This Nuxt plugin wraps the global `$fetch` to intercept 401 responses.
|
|
* When a 401 is detected, it:
|
|
* 1. Calls the auth store's logout() method (clears tokens)
|
|
* 2. Redirects to /login
|
|
*
|
|
* This fixes the P0 bug where expired tokens did not force a logout on page reload.
|
|
*/
|
|
|
|
import { defineNuxtPlugin, navigateTo, useCookie } from '#app'
|
|
import { useAuthStore } from '~/stores/auth'
|
|
|
|
export default defineNuxtPlugin(() => {
|
|
// We hook into the global $fetch via onResponse interceptor
|
|
// Nuxt's $fetch is built on ofetch which supports interceptors
|
|
|
|
const originalFetch = globalThis.fetch
|
|
|
|
// Override global fetch to intercept 401 responses
|
|
globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
const response = await originalFetch(input, init)
|
|
|
|
// Intercept 401 Unauthorized
|
|
if (response.status === 401) {
|
|
// Avoid intercepting login endpoint itself (would cause redirect loops)
|
|
const url = typeof input === 'string' ? input : (input instanceof URL ? input.href : input.url)
|
|
if (url.includes('/auth/login') || url.includes('/auth/refresh')) {
|
|
return response
|
|
}
|
|
|
|
// Clear auth state
|
|
try {
|
|
const authStore = useAuthStore()
|
|
authStore.logout()
|
|
} catch {
|
|
// Fallback: manually clear the cookie
|
|
const tokenCookie = useCookie('access_token')
|
|
tokenCookie.value = null
|
|
}
|
|
|
|
// Redirect to login
|
|
if (process.client) {
|
|
// Use window.location for hard redirect to ensure full state reset
|
|
window.location.href = '/login'
|
|
} else {
|
|
await navigateTo('/login')
|
|
}
|
|
}
|
|
|
|
return response
|
|
}
|
|
})
|