// Copied from https://github.com/atinux/nuxt-auth-utils/issues/91#issuecomment-2476019136 import { appendResponseHeader } from 'h3' import { parse, parseSetCookie, serialize } from 'cookie-es' import { jwtDecode, type JwtPayload } from 'jwt-decode' export default defineNuxtRouteMiddleware(async (to, from) => { const nuxtApp = useNuxtApp() // Don't run on client hydration when server rendered if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return console.log('🔍 Middleware: refreshToken.global.ts') console.log(` from: ${from.fullPath} to: ${to.fullPath}`) const { session, clear: clearSession, fetch: fetchSession } = useUserSession() // Ignore if no tokens if (!session.value?.jwt) return const serverEvent = useRequestEvent() const runtimeConfig = useRuntimeConfig() const { accessToken, refreshToken } = session.value.jwt const accessPayload = jwtDecode(accessToken) const refreshPayload = jwtDecode(refreshToken) // Both tokens expired, clearing session if (isExpired(accessPayload) && isExpired(refreshPayload)) { console.info('both tokens expired, clearing session') await clearSession() return navigateTo('/login') } else if (isExpired(accessPayload)) { console.info('access token expired, refreshing') await useRequestFetch()('/api/jwt/refresh', { method: 'POST', onResponse({ response: { headers } }) { // Forward the Set-Cookie header to the main server event if (import.meta.server && serverEvent) { for (const setCookie of headers.getSetCookie()) { appendResponseHeader(serverEvent, 'Set-Cookie', setCookie) // Update session cookie for next fetch requests const { name, value } = parseSetCookie(setCookie) if (name === runtimeConfig.session.name) { console.log('updating headers.cookie to', value) const cookies = parse(serverEvent.headers.get('cookie') || '') // set or overwrite existing cookie cookies[name] = value // update cookie event header for future requests serverEvent.headers.set( 'cookie', Object.entries(cookies) .map(([name, value]) => serialize(name, value)) .join('; ') ) // Also apply to serverEvent.node.req.headers if (serverEvent.node?.req?.headers) { serverEvent.node.req.headers['cookie'] = serverEvent.headers.get('cookie') || '' } } } } }, onError() { console.error('🔍 Middleware: Token refresh failed') const { loggedIn } = useUserSession() if (!loggedIn.value) { console.log('🔍 Middleware: User not logged in, redirecting to /login') return navigateTo('/login') } } }) // Refresh the session await fetchSession() } }) function isExpired(payload: JwtPayload) { return payload?.exp && payload.exp < Date.now() / 1000 }