84 lines
3.0 KiB
TypeScript
84 lines
3.0 KiB
TypeScript
// 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
|
|
}
|