Skip to content

Commit 0d3181c

Browse files
fix(#970): make redirect implementation match Nuxt's (#985)
Co-authored-by: Zoey <[email protected]>
1 parent 85034e0 commit 0d3181c

File tree

1 file changed

+35
-9
lines changed

1 file changed

+35
-9
lines changed
Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { sanitizeStatusCode } from 'h3'
2-
import { type NuxtApp, abortNavigation, callWithNuxt, useNuxtApp } from '#app'
1+
import { hasProtocol, isScriptProtocol, joinURL } from 'ufo'
2+
import { type NuxtApp, abortNavigation, callWithNuxt, useNuxtApp, useRouter, useRuntimeConfig } from '#app'
33

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

8+
const URL_QUOTE_RE = /"/g
9+
810
/**
911
* 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.
1012
*
@@ -13,21 +15,33 @@ export function navigateToAuthPageWN(nuxt: NuxtApp, href: string) {
1315
* 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)**.
1416
* 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.
1517
*
16-
* Adapted from: https://github.com/nuxt/nuxt/blob/d188542a35bb541c7ed2e4502c687c2132979882/packages/nuxt/src/app/composables/router.ts#L161-L188
18+
* Adapted from https://github.com/nuxt/nuxt/blob/16d213bbdcc69c0cc72afb355755ff877654a374/packages/nuxt/src/app/composables/router.ts#L119-L217
1719
*
1820
* @param href HREF / URL to navigate to
1921
*/
2022
export function navigateToAuthPage(href: string) {
23+
const router = useRouter()
2124
const nuxtApp = useNuxtApp()
2225

2326
if (import.meta.server) {
2427
if (nuxtApp.ssrContext) {
28+
const isExternalHost = hasProtocol(href, { acceptRelative: true })
29+
if (isExternalHost) {
30+
const { protocol } = new URL(href, 'http://localhost')
31+
if (protocol && isScriptProtocol(protocol)) {
32+
throw new Error(`Cannot navigate to a URL with '${protocol}' protocol.`)
33+
}
34+
}
35+
36+
const fullPath = isExternalHost ? href : router.resolve(href).fullPath || '/'
37+
const location = isExternalHost ? href : joinURL(useRuntimeConfig().app.baseURL, fullPath)
38+
2539
// TODO: consider deprecating in favour of `app:rendered` and removing
2640
return nuxtApp.callHook('app:redirected').then(() => {
27-
const encodedLoc = href.replace(/"/g, '%22')
28-
const encodedHeader = new URL(href).toString()
41+
const encodedLoc = location.replace(URL_QUOTE_RE, '%22')
42+
const encodedHeader = encodeURL(location, isExternalHost)
2943
nuxtApp.ssrContext!._renderResponse = {
30-
statusCode: sanitizeStatusCode(302, 302),
44+
statusCode: 302,
3145
body: `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head></html>`,
3246
headers: { location: encodedHeader },
3347
}
@@ -42,12 +56,24 @@ export function navigateToAuthPage(href: string) {
4256
window.location.reload()
4357
}
4458

45-
// 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
46-
const router = nuxtApp.$router as { push: (href: string) => void }
47-
4859
// 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)
4960
const waitForNavigationWithFallbackToRouter = new Promise(resolve => setTimeout(resolve, 60 * 1000))
5061
.then(() => router.push(href))
5162

5263
return waitForNavigationWithFallbackToRouter as Promise<void | undefined>
5364
}
65+
66+
/**
67+
* Adapted from https://github.com/nuxt/nuxt/blob/16d213bbdcc69c0cc72afb355755ff877654a374/packages/nuxt/src/app/composables/router.ts#L270C1-L282C2
68+
* @internal
69+
*/
70+
export function encodeURL(location: string, isExternalHost = false) {
71+
const url = new URL(location, 'http://localhost')
72+
if (!isExternalHost) {
73+
return url.pathname + url.search + url.hash
74+
}
75+
if (location.startsWith('//')) {
76+
return url.toString().replace(url.protocol, '')
77+
}
78+
return url.toString()
79+
}

0 commit comments

Comments
 (0)