Files
gremiumhub/legalconsenthub/app/middleware/refreshToken.global.ts

86 lines
3.2 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'
import { useLogger } from '../composables/useLogger'
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
const logger = useLogger().withTag('refreshToken')
logger.debug('🔍 Middleware: refreshToken.global.ts')
logger.debug(`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)) {
logger.info('both tokens expired, clearing session')
await clearSession()
return navigateTo('/login')
} else if (isExpired(accessPayload)) {
logger.info('access token expired, refreshing')
await useRequestFetch()('/api/jwt/refresh', {
method: 'POST',
onResponse({ response: { headers } }: { response: { headers: 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) {
logger.debug('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() {
logger.error('🔍 Middleware: Token refresh failed')
const { loggedIn } = useUserSession()
if (!loggedIn.value) {
logger.debug('🔍 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
}