From f3762b76f2da96b32bae37700b80f5890d312e07 Mon Sep 17 00:00:00 2001 From: Marsel Shayhin Date: Thu, 21 Nov 2024 17:33:49 +0100 Subject: [PATCH] fix: fix infinite recursion when trying to do auth on error pages --- src/runtime/composables/authjs/useAuth.ts | 2 +- src/runtime/middleware/auth.ts | 2 +- src/runtime/plugin.ts | 21 ++++++++++++++++++--- src/runtime/utils/fetch.ts | 21 ++++++++++++++++++--- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/runtime/composables/authjs/useAuth.ts b/src/runtime/composables/authjs/useAuth.ts index 774ffb8f..85e1279d 100644 --- a/src/runtime/composables/authjs/useAuth.ts +++ b/src/runtime/composables/authjs/useAuth.ts @@ -164,7 +164,7 @@ function getProviders() { * * @param getSessionOptions - Options for getting the session, e.g., set `required: true` to enforce that a session _must_ exist, the user will be directed to a login page otherwise. */ -async function getSession(getSessionOptions?: GetSessionOptions): Promise { +async function getSession(getSessionOptions?: GetSessionOptions): Promise { const nuxt = useNuxtApp() const callbackUrlFallback = await getRequestURLWN(nuxt) diff --git a/src/runtime/middleware/auth.ts b/src/runtime/middleware/auth.ts index d66e8a87..094ed2d1 100644 --- a/src/runtime/middleware/auth.ts +++ b/src/runtime/middleware/auth.ts @@ -73,7 +73,7 @@ export default defineNuxtRouteMiddleware((to) => { * We do not want to enforce protection on `404` pages (unless the user opts out of it by setting `allow404WithoutAuth: false`). * * This is to: - * - improve UX and DX: Having to log-in to see a `404` is not pleasent, + * - improve UX and DX: Having to log-in to see a `404` is not pleasant, * - avoid the `Error [ERR_HTTP_HEADERS_SENT]`-error that occurs when we redirect to the sign-in page when the original to-page does not exist. Likely related to https://github.com/nuxt/framework/issues/9438 * */ diff --git a/src/runtime/plugin.ts b/src/runtime/plugin.ts index 75952fad..1e77daea 100644 --- a/src/runtime/plugin.ts +++ b/src/runtime/plugin.ts @@ -2,6 +2,7 @@ import { getHeader } from 'h3' import authMiddleware from './middleware/auth' import { getNitroRouteRules } from './utils/kit' import { _refreshHandler, addRouteMiddleware, defineNuxtPlugin, useAuth, useAuthState, useRuntimeConfig } from '#imports' +import { FetchConfigurationError } from './utils/fetch' export default defineNuxtPlugin(async (nuxtApp) => { // 1. Initialize authentication state, potentially fetch current session @@ -10,6 +11,7 @@ export default defineNuxtPlugin(async (nuxtApp) => { // use runtimeConfig const runtimeConfig = useRuntimeConfig().public.auth + const globalAppMiddleware = runtimeConfig.globalAppMiddleware const routeRules = import.meta.server ? getNitroRouteRules(nuxtApp._route.path) : {} @@ -30,8 +32,22 @@ export default defineNuxtPlugin(async (nuxtApp) => { } // Only fetch session if it was not yet initialized server-side - if (typeof data.value === 'undefined' && !nitroPrerender && !disableServerSideAuth) { - await getSession() + const isErrorUrl = nuxtApp.ssrContext?.error === true + const requireAuthOnErrorPage = globalAppMiddleware === true || (typeof globalAppMiddleware === 'object' && globalAppMiddleware.allow404WithoutAuth) + const shouldFetchSession = typeof data.value === 'undefined' + && !nitroPrerender + && !disableServerSideAuth + && !(isErrorUrl && requireAuthOnErrorPage) + + if (shouldFetchSession) { + try { + await getSession() + } catch (e) { + // Do not throw the configuration error as it can lead to infinite recursion + if (!(e instanceof FetchConfigurationError)) { + throw e + } + } } // 2. Setup session maintanence, e.g., auto refreshing or refreshing on foux @@ -55,7 +71,6 @@ export default defineNuxtPlugin(async (nuxtApp) => { } // 3. Enable the middleware, either globally or as a named `auth` option - const { globalAppMiddleware } = useRuntimeConfig().public.auth if ( globalAppMiddleware === true || (typeof globalAppMiddleware === 'object' && globalAppMiddleware.isEnabled) diff --git a/src/runtime/utils/fetch.ts b/src/runtime/utils/fetch.ts index 1c5db95f..eca58272 100644 --- a/src/runtime/utils/fetch.ts +++ b/src/runtime/utils/fetch.ts @@ -2,14 +2,27 @@ import { resolveApiUrlPath } from './url' import { callWithNuxt, useRuntimeConfig } from '#app' import type { useNuxtApp } from '#imports' +const ERROR_PREFIX = '[@sidebase/nuxt-auth]' + export async function _fetch(nuxt: ReturnType, path: string, fetchOptions?: Parameters[1]): Promise { const runtimeConfig = await callWithNuxt(nuxt, useRuntimeConfig) const joinedPath = resolveApiUrlPath(path, runtimeConfig) + + // Prevent callback recursion when doing internal routing + if (runtimeConfig.public.auth.disableInternalRouting === false) { + const currentPath = nuxt.ssrContext?.event?.path + // const isErrorUrl = nuxt.ssrContext?.error && + if (currentPath?.startsWith(joinedPath)) { + console.error(`${ERROR_PREFIX} Recursion detected at ${joinedPath}. Have you set the correct \`auth.baseURL\`?`) + throw new FetchConfigurationError('Server configuration error') + } + } + try { return $fetch(joinedPath, fetchOptions) } catch (error) { - let errorMessage = `[@sidebase/nuxt-auth] Error while requesting ${joinedPath}.` + let errorMessage = `${ERROR_PREFIX} Error while requesting ${joinedPath}.` if (runtimeConfig.public.auth.provider.type === 'authjs') { errorMessage += ' Have you added the authentication handler server-endpoint `[...].ts`? Have you added the authentication handler in a non-default location (default is `~/server/api/auth/[...].ts`) and not updated the module-setting `auth.basePath`?' } @@ -17,8 +30,10 @@ export async function _fetch(nuxt: ReturnType, path: strin console.error(errorMessage) console.error(error) - throw new Error( - 'Runtime error, checkout the console logs to debug, open an issue at https://github.com/sidebase/nuxt-auth/issues/new/choose if you continue to have this problem' + throw new FetchConfigurationError( + 'Runtime error, check the console logs to debug, open an issue at https://github.com/sidebase/nuxt-auth/issues/new/choose if you continue to have this problem' ) } } + +export class FetchConfigurationError extends Error {}