Skip to content

Commit

Permalink
fix(#970): make redirect implementation match Nuxt's (#985)
Browse files Browse the repository at this point in the history
Co-authored-by: Zoey <[email protected]>
  • Loading branch information
phoenix-ru and zoey-kaiser authored Jan 16, 2025
1 parent 85034e0 commit 0d3181c
Showing 1 changed file with 35 additions and 9 deletions.
44 changes: 35 additions & 9 deletions src/runtime/composables/authjs/utils/navigateToAuthPage.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { sanitizeStatusCode } from 'h3'
import { type NuxtApp, abortNavigation, callWithNuxt, useNuxtApp } from '#app'
import { hasProtocol, isScriptProtocol, joinURL } from 'ufo'
import { type NuxtApp, abortNavigation, callWithNuxt, useNuxtApp, useRouter, useRuntimeConfig } from '#app'

export function navigateToAuthPageWN(nuxt: NuxtApp, href: string) {
return callWithNuxt(nuxt, navigateToAuthPage, [href])
}

const URL_QUOTE_RE = /"/g

/**
* Function to correctly navigate to auth-routes, necessary as the auth-routes are not part of the nuxt-app itself, so unknown to nuxt / vue-router.
*
Expand All @@ -13,21 +15,33 @@ export function navigateToAuthPageWN(nuxt: NuxtApp, href: string) {
* manually set `window.location.href` on the client **and then fake return a Promise that does not immediately resolve to block navigation (although it will not actually be fully awaited, but just be awaited long enough for the naviation to complete)**.
* 2. Additionally on the server-side, we cannot use `navigateTo(signInUrl)` as this uses `vue-router` internally which does not know the "external" sign-in page of next-auth and thus will log a warning which we want to avoid.
*
* Adapted from: https://github.com/nuxt/nuxt/blob/d188542a35bb541c7ed2e4502c687c2132979882/packages/nuxt/src/app/composables/router.ts#L161-L188
* Adapted from https://github.com/nuxt/nuxt/blob/16d213bbdcc69c0cc72afb355755ff877654a374/packages/nuxt/src/app/composables/router.ts#L119-L217
*
* @param href HREF / URL to navigate to
*/
export function navigateToAuthPage(href: string) {
const router = useRouter()
const nuxtApp = useNuxtApp()

if (import.meta.server) {
if (nuxtApp.ssrContext) {
const isExternalHost = hasProtocol(href, { acceptRelative: true })
if (isExternalHost) {
const { protocol } = new URL(href, 'http://localhost')
if (protocol && isScriptProtocol(protocol)) {
throw new Error(`Cannot navigate to a URL with '${protocol}' protocol.`)
}
}

const fullPath = isExternalHost ? href : router.resolve(href).fullPath || '/'
const location = isExternalHost ? href : joinURL(useRuntimeConfig().app.baseURL, fullPath)

// TODO: consider deprecating in favour of `app:rendered` and removing
return nuxtApp.callHook('app:redirected').then(() => {
const encodedLoc = href.replace(/"/g, '%22')
const encodedHeader = new URL(href).toString()
const encodedLoc = location.replace(URL_QUOTE_RE, '%22')
const encodedHeader = encodeURL(location, isExternalHost)
nuxtApp.ssrContext!._renderResponse = {
statusCode: sanitizeStatusCode(302, 302),
statusCode: 302,
body: `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head></html>`,
headers: { location: encodedHeader },
}
Expand All @@ -42,12 +56,24 @@ export function navigateToAuthPage(href: string) {
window.location.reload()
}

// TODO: Sadly, we cannot directly import types from `vue-router` as it leads to build failures. Typing the router about should help us to avoid manually typing `route` below
const router = nuxtApp.$router as { push: (href: string) => void }

// Wait for the `window.location.href` navigation from above to complete to avoid showing content. If that doesn't work fast enough, delegate navigation back to the `vue-router` (risking a vue-router 404 warning in the console, but still avoiding content-flashes of the protected target page)
const waitForNavigationWithFallbackToRouter = new Promise(resolve => setTimeout(resolve, 60 * 1000))
.then(() => router.push(href))

return waitForNavigationWithFallbackToRouter as Promise<void | undefined>
}

/**
* Adapted from https://github.com/nuxt/nuxt/blob/16d213bbdcc69c0cc72afb355755ff877654a374/packages/nuxt/src/app/composables/router.ts#L270C1-L282C2
* @internal
*/
export function encodeURL(location: string, isExternalHost = false) {
const url = new URL(location, 'http://localhost')
if (!isExternalHost) {
return url.pathname + url.search + url.hash
}
if (location.startsWith('//')) {
return url.toString().replace(url.protocol, '')
}
return url.toString()
}

0 comments on commit 0d3181c

Please sign in to comment.