From 1cd7381ee910af35e94da6bcaf6a217f57ee7013 Mon Sep 17 00:00:00 2001 From: Marsel Shaikhin Date: Thu, 7 Nov 2024 18:59:35 +0100 Subject: [PATCH] chore: major refactoring --- src/module.ts | 9 +- src/runtime/composables/authjs/useAuth.ts | 21 ++-- .../authjs/utils/navigateToAuthPage.ts | 53 +++++++++ .../composables/common/getRequestURL.ts | 10 ++ src/runtime/composables/commonAuthState.ts | 6 - src/runtime/composables/local/useAuth.ts | 2 +- src/runtime/composables/local/useAuthState.ts | 3 - src/runtime/composables/local/utils/url.ts | 41 ------- src/runtime/middleware/auth.ts | 3 +- .../server/services/authjs/nuxtAuthHandler.ts | 8 +- src/runtime/server/services/utils.ts | 22 ++-- src/runtime/types.ts | 16 +++ src/runtime/utils/callWithNuxt.ts | 9 -- src/runtime/utils/fetch.ts | 17 +-- src/runtime/utils/url.ts | 104 +++++++++--------- tests/local.url.spec.ts | 3 +- 16 files changed, 180 insertions(+), 147 deletions(-) create mode 100644 src/runtime/composables/authjs/utils/navigateToAuthPage.ts create mode 100644 src/runtime/composables/common/getRequestURL.ts delete mode 100644 src/runtime/composables/local/utils/url.ts delete mode 100644 src/runtime/utils/callWithNuxt.ts diff --git a/src/module.ts b/src/module.ts index 79d8543f..a2eef682 100644 --- a/src/module.ts +++ b/src/module.ts @@ -26,6 +26,7 @@ import type { const topLevelDefaults = { isEnabled: true, baseURL: '/api/auth', + disableInternalRouting: false as boolean, disableServerSideAuth: false, originEnvKey: 'AUTH_ORIGIN', sessionRefresh: { @@ -126,7 +127,13 @@ export default defineNuxtModule({ logger.info('`nuxt-auth` setup starting') - // 2. Set up runtime configuration + // 2.1. Disable internal routing for `local` provider when not specified otherwise + // https://github.com/sidebase/nuxt-auth/issues/797 + if (userOptions.disableInternalRouting === undefined && selectedProvider === 'local') { + options.disableInternalRouting = true + } + + // 2.2. Set up runtime configuration if (!isProduction) { const loggerMessages = [ `Selected provider: ${selectedProvider}.`, diff --git a/src/runtime/composables/authjs/useAuth.ts b/src/runtime/composables/authjs/useAuth.ts index 78ceb585..774ffb8f 100644 --- a/src/runtime/composables/authjs/useAuth.ts +++ b/src/runtime/composables/authjs/useAuth.ts @@ -2,13 +2,14 @@ import type { AppProvider, BuiltInProviderType } from 'next-auth/providers/index import { defu } from 'defu' import { type Ref, readonly } from 'vue' import { appendHeader } from 'h3' -import { determineCallbackUrl } from '../../utils/url' -import { getRequestURLWN, joinPathToApiURLWN, makeCWN, navigateToAuthPageWN } from '../../utils/callWithNuxt' +import { determineCallbackUrl, resolveApiUrlPath } from '../../utils/url' import { _fetch } from '../../utils/fetch' import { isNonEmptyObject } from '../../utils/checkSessionResult' import type { CommonUseAuthReturn, GetSessionOptions, SignInFunc, SignOutFunc } from '../../types' import { useTypedBackendConfig } from '../../helpers' +import { getRequestURLWN } from '../common/getRequestURL' import type { SessionData } from './useAuthState' +import { navigateToAuthPageWN } from './utils/navigateToAuthPage' import type { NuxtApp } from '#app/nuxt' import { callWithNuxt } from '#app/nuxt' import { createError, useAuthState, useNuxtApp, useRequestHeaders, useRuntimeConfig } from '#imports' @@ -49,7 +50,9 @@ async function getCsrfToken() { const headers = await getRequestCookies(nuxt) return _fetch<{ csrfToken: string }>(nuxt, '/csrf', { headers }).then(response => response.csrfToken) } -const getCsrfTokenWithNuxt = makeCWN(getCsrfToken) +function getCsrfTokenWithNuxt(nuxt: NuxtApp) { + return callWithNuxt(nuxt, getCsrfToken) +} /** * Trigger a sign in flow for the passed `provider`. If no provider is given the sign in page for all providers will be shown. @@ -61,17 +64,16 @@ const getCsrfTokenWithNuxt = makeCWN(getCsrfToken) type SignInResult = void | { error: string | null, status: number, ok: boolean, url: any } const signIn: SignInFunc = async (provider, options, authorizationParams) => { const nuxt = useNuxtApp() + const runtimeConfig = await callWithNuxt(nuxt, useRuntimeConfig) // 1. Lead to error page if no providers are available const configuredProviders = await getProviders() if (!configuredProviders) { - const errorUrl = await joinPathToApiURLWN(nuxt, 'error') + const errorUrl = resolveApiUrlPath('error', runtimeConfig) return navigateToAuthPageWN(nuxt, errorUrl) } // 2. If no `provider` was given, either use the configured `defaultProvider` or `undefined` (leading to a forward to the `/login` page with all providers) - const runtimeConfig = await callWithNuxt(nuxt, useRuntimeConfig) - const backendConfig = useTypedBackendConfig(runtimeConfig, 'authjs') if (typeof provider === 'undefined') { // NOTE: `provider` might be an empty string @@ -87,7 +89,7 @@ const signIn: SignInFunc = async (provider, op callbackUrl = await determineCallbackUrl(runtimeConfig.public.auth, () => getRequestURLWN(nuxt)) } - const signinUrl = await joinPathToApiURLWN(nuxt, 'signin') + const signinUrl = resolveApiUrlPath('signin', runtimeConfig) const queryParams = callbackUrl ? `?${new URLSearchParams({ callbackUrl })}` : '' const hrefSignInAllProviderPage = `${signinUrl}${queryParams}` @@ -140,7 +142,6 @@ const signIn: SignInFunc = async (provider, op // At this point the request succeeded (i.e., it went through) const error = new URL(data.url).searchParams.get('error') - // eslint-disable-next-line ts/no-use-before-define await getSessionWithNuxt(nuxt) return { @@ -222,7 +223,9 @@ async function getSession(getSessionOptions?: GetSessionOptions): Promise { + const encodedLoc = href.replace(/"/g, '%22') + const encodedHeader = new URL(href).toString() + nuxtApp.ssrContext!._renderResponse = { + statusCode: sanitizeStatusCode(302, 302), + body: ``, + headers: { location: encodedHeader }, + } + abortNavigation() + }) + } + } + + window.location.href = href + // If href contains a hash, the browser does not reload the page. We reload manually. + if (href.includes('#')) { + 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 +} diff --git a/src/runtime/composables/common/getRequestURL.ts b/src/runtime/composables/common/getRequestURL.ts new file mode 100644 index 00000000..413a32fc --- /dev/null +++ b/src/runtime/composables/common/getRequestURL.ts @@ -0,0 +1,10 @@ +import getURL from 'requrl' +import { type NuxtApp, callWithNuxt, useRequestEvent } from '#app' + +export function getRequestURL(includePath = true) { + return getURL(useRequestEvent()?.node.req, includePath) +} + +export function getRequestURLWN(nuxt: NuxtApp) { + return callWithNuxt(nuxt, getRequestURL) +} diff --git a/src/runtime/composables/commonAuthState.ts b/src/runtime/composables/commonAuthState.ts index 63ba53d3..7dfab72b 100644 --- a/src/runtime/composables/commonAuthState.ts +++ b/src/runtime/composables/commonAuthState.ts @@ -28,16 +28,10 @@ export function makeCommonAuthState() { return 'unauthenticated' }) - const { origin, pathname } = useRuntimeConfig().public.auth.computed - return { data, loading, lastRefreshedAt, status, - _internal: { - origin, - pathname - } } } diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index d2a20028..af36a028 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -3,8 +3,8 @@ import { type Ref, readonly } from 'vue' import type { CommonUseAuthReturn, GetSessionOptions, SecondarySignInOptions, SignInFunc, SignOutFunc, SignUpOptions } from '../../types' import { jsonPointerGet, objectFromJsonPointer, useTypedBackendConfig } from '../../helpers' import { _fetch } from '../../utils/fetch' -import { getRequestURLWN } from '../../utils/callWithNuxt' import { determineCallbackUrl } from '../../utils/url' +import { getRequestURLWN } from '../common/getRequestURL' import { formatToken } from './utils/token' import { type UseAuthStateReturn, useAuthState } from './useAuthState' import { callWithNuxt } from '#app/nuxt' diff --git a/src/runtime/composables/local/useAuthState.ts b/src/runtime/composables/local/useAuthState.ts index 8b751992..5dcb0c73 100644 --- a/src/runtime/composables/local/useAuthState.ts +++ b/src/runtime/composables/local/useAuthState.ts @@ -22,8 +22,6 @@ export interface UseAuthStateReturn extends CommonUseAuthStateReturn void clearToken: () => void _internal: { - origin?: string - pathname: string rawTokenCookie: CookieRef } } @@ -108,7 +106,6 @@ export function useAuthState(): UseAuthStateReturn { setToken, clearToken, _internal: { - ...commonAuthState._internal, rawTokenCookie: _rawTokenCookie } } diff --git a/src/runtime/composables/local/utils/url.ts b/src/runtime/composables/local/utils/url.ts deleted file mode 100644 index 9ab37d81..00000000 --- a/src/runtime/composables/local/utils/url.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { joinURL } from 'ufo' - -// Slimmed down type to allow easy unit testing -interface RuntimeConfig { - public: { - auth: { - baseURL: string - originEnvKey: string - } - } -} - -// TODO Comment below: remove/rewrite/add to docs -// Prios in runtime (baseURL): -// - env variable (using `originEnvKey`); -// - static runtime config (`baseURL` captured during build); -// - defaults; -export function resolveApiUrlPath( - endpointPath: string, - runtimeConfig: RuntimeConfig -): string { - // Fully-specified endpoint path - do not join with `baseURL` - if (endpointPath.startsWith('http://') || endpointPath.startsWith('https://')) { - return endpointPath - } - - const authRuntimeConfig = runtimeConfig.public.auth - - // Default to static runtime config (still overridable using `NUXT_PUBLIC_AUTH_BASE_URL`) - let baseURL = authRuntimeConfig.baseURL - - // Override base URL using environment variable specified in `originEnvKey` if any - if (authRuntimeConfig.originEnvKey) { - const envBaseURL = process.env[authRuntimeConfig.originEnvKey] - if (envBaseURL) { - baseURL = envBaseURL - } - } - - return joinURL(baseURL, endpointPath) -} diff --git a/src/runtime/middleware/auth.ts b/src/runtime/middleware/auth.ts index b1cb4992..d66e8a87 100644 --- a/src/runtime/middleware/auth.ts +++ b/src/runtime/middleware/auth.ts @@ -1,4 +1,3 @@ -import type { navigateToAuthPages } from '../utils/url' import { determineCallbackUrl } from '../utils/url' import { isProduction } from '../helpers' import { defineNuxtRouteMiddleware, navigateTo, useAuth, useRuntimeConfig } from '#imports' @@ -91,7 +90,7 @@ export default defineNuxtRouteMiddleware((to) => { const signInOptions: Parameters[1] = { error: 'SessionRequired', callbackUrl: determineCallbackUrl(authConfig, () => to.fullPath) } // eslint-disable-next-line ts/ban-ts-comment // @ts-ignore This is valid for a backend-type of `authjs`, where sign-in accepts a provider as a first argument - return signIn(undefined, signInOptions) as ReturnType + return signIn(undefined, signInOptions) as Promise } // Redirect path was provided diff --git a/src/runtime/server/services/authjs/nuxtAuthHandler.ts b/src/runtime/server/services/authjs/nuxtAuthHandler.ts index 82e845a6..7934054b 100644 --- a/src/runtime/server/services/authjs/nuxtAuthHandler.ts +++ b/src/runtime/server/services/authjs/nuxtAuthHandler.ts @@ -15,7 +15,7 @@ import { ERROR_MESSAGES } from '../errors' import { isNonEmptyObject } from '../../../utils/checkSessionResult' import { getServerOrigin } from '../utils' import { useTypedBackendConfig } from '../../../helpers' - +import { resolveApiBaseURL } from '../../../utils/url' import { useRuntimeConfig } from '#imports' let preparedAuthjsHandler: ((req: RequestInternal) => Promise) | undefined @@ -102,15 +102,15 @@ export function NuxtAuthHandler(nuxtAuthOptions?: AuthOptions) { /** Gets session on server-side */ export async function getServerSession(event: H3Event) { const runtimeConfig = useRuntimeConfig() - const authBasePath = runtimeConfig.public.auth.computed.pathname + const authBasePathname = resolveApiBaseURL(runtimeConfig, true) const trustHostUserPreference = useTypedBackendConfig(runtimeConfig, 'authjs').trustHost // avoid running auth middleware on auth middleware (see #186) - if (event.path && event.path.startsWith(authBasePath)) { + if (event.path && event.path.startsWith(authBasePathname)) { return null } - const sessionUrlPath = joinURL(authBasePath, '/session') + const sessionUrlPath = joinURL(authBasePathname, '/session') const headers = getHeaders(event) as HeadersInit if (!preparedAuthjsHandler) { // Edge-case: If no auth-endpoint was called yet, `preparedAuthHandler`-initialization was also not attempted as Nuxt lazily loads endpoints in production-mode. diff --git a/src/runtime/server/services/utils.ts b/src/runtime/server/services/utils.ts index 7672f00f..7cbe5446 100644 --- a/src/runtime/server/services/utils.ts +++ b/src/runtime/server/services/utils.ts @@ -1,7 +1,8 @@ import type { H3Event } from 'h3' import getURL from 'requrl' +import { parseURL } from 'ufo' import { isProduction } from '../../helpers' -import { extractFromRuntimeConfig } from '../../utils/extractFromRuntimeConfig' +import { resolveApiBaseURL } from '../../utils/url' import { ERROR_MESSAGES } from './errors' import { useRuntimeConfig } from '#imports' @@ -9,20 +10,17 @@ import { useRuntimeConfig } from '#imports' * Get `origin` and fallback to `x-forwarded-host` or `host` headers if not in production. */ export function getServerOrigin(event?: H3Event): string { - const config = useRuntimeConfig() + const runtimeConfig = useRuntimeConfig() // Prio 1: Environment variable - const envOriginKey = config.public.auth.originEnvKey - const envFromRuntimeConfig = extractFromRuntimeConfig(config, envOriginKey) - const envOrigin = envFromRuntimeConfig ?? process.env[envOriginKey] - if (envOrigin) { - return envOrigin - } + // Prio 2: Static configuration - // Prio 2: Computed origin - const runtimeConfigOrigin = config.public.auth.computed.origin - if (runtimeConfigOrigin) { - return runtimeConfigOrigin + // Resolve the value from runtime config/env. + // If the returned value has protocol and host, it is considered valid. + const baseURL = resolveApiBaseURL(runtimeConfig, false) + const parsed = parseURL(baseURL) + if (parsed.protocol && parsed.host) { + return `${parsed.protocol}//${parsed.host}` } // Prio 3: Try to infer the origin if we're not in production diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 9c251c5e..cce172c4 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -409,6 +409,21 @@ export interface ModuleOptions { * Whether the module is enabled at all */ isEnabled?: boolean + /** + * Disables the Nuxt `$fetch` optimization. Do so when your auth logic is not handled by a Nuxt server (e.g. when using an external backend). + * + * Disabling the optimisation means that NuxtAuth will prefer calling `baseURL` + path instead of just path, + * which would often translate to an HTTP call. + * + * By default, this option is set to `false` for `authjs` provider. + * For `local` provider `disableInternalRouting` will default to `true` unless explicitly changed by user. + * + * ## Example + * With `disableInternalRouting: true` and `baseURL: 'https://example.com/api/auth'` your calls would be made to `https://example.com/api/auth` endpoints instead of `/api/auth`. + * + * @see https://nuxt.com/docs/api/utils/dollarfetch + */ + disableInternalRouting?: boolean /** * Forces your server to send a "loading" status on all requests, prompting the client to fetch on the client. If your website has caching, this prevents the server from caching someone's authentication status. * @@ -586,6 +601,7 @@ export type SignInFunc = ( export interface ModuleOptionsNormalized extends ModuleOptions { isEnabled: boolean baseURL: string + disableInternalRouting: boolean // Cannot use `DeepRequired` here because it leads to build issues provider: Required> sessionRefresh: NonNullable diff --git a/src/runtime/utils/callWithNuxt.ts b/src/runtime/utils/callWithNuxt.ts deleted file mode 100644 index 6f601782..00000000 --- a/src/runtime/utils/callWithNuxt.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { getRequestURL, joinPathToApiURL, navigateToAuthPages } from './url' -import type { NuxtApp } from '#app/nuxt' -import { callWithNuxt } from '#app/nuxt' - -export const navigateToAuthPageWN = (nuxt: NuxtApp, href: string) => callWithNuxt(nuxt, navigateToAuthPages, [href]) -export const getRequestURLWN = (nuxt: NuxtApp) => callWithNuxt(nuxt, getRequestURL) -export const joinPathToApiURLWN = (nuxt: NuxtApp, path: string) => callWithNuxt(nuxt, joinPathToApiURL, [path]) - -export const makeCWN = (func: (...args: any) => unknown) => (nuxt: NuxtApp) => callWithNuxt(nuxt, func) diff --git a/src/runtime/utils/fetch.ts b/src/runtime/utils/fetch.ts index aa0e2488..1c5db95f 100644 --- a/src/runtime/utils/fetch.ts +++ b/src/runtime/utils/fetch.ts @@ -1,17 +1,20 @@ -import { joinPathToApiURL } from './url' -import { callWithNuxt } from '#app/nuxt' +import { resolveApiUrlPath } from './url' +import { callWithNuxt, useRuntimeConfig } from '#app' import type { useNuxtApp } from '#imports' export async function _fetch(nuxt: ReturnType, path: string, fetchOptions?: Parameters[1]): Promise { + const runtimeConfig = await callWithNuxt(nuxt, useRuntimeConfig) + const joinedPath = resolveApiUrlPath(path, runtimeConfig) try { - const joinedPath = await callWithNuxt(nuxt, () => joinPathToApiURL(path)) return $fetch(joinedPath, fetchOptions) } catch (error) { - // TODO: Adapt this error to be more generic - console.error( - 'Error in `nuxt-auth`-app-side data fetching: 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`? Error is:' - ) + let errorMessage = `[@sidebase/nuxt-auth] 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`?' + } + errorMessage += ' Error is:' + console.error(errorMessage) console.error(error) throw new Error( diff --git a/src/runtime/utils/url.ts b/src/runtime/utils/url.ts index e80042f6..1504279c 100644 --- a/src/runtime/utils/url.ts +++ b/src/runtime/utils/url.ts @@ -1,76 +1,78 @@ -import { joinURL } from 'ufo' -import getURL from 'requrl' -import { sanitizeStatusCode } from 'h3' -import type { ModuleOptionsNormalized } from '../types' -import { abortNavigation, useAuthState, useNuxtApp, useRequestEvent } from '#imports' +import { joinURL, parsePath } from 'ufo' -export const getRequestURL = (includePath = true) => getURL(useRequestEvent()?.node.req, includePath) -export function joinPathToApiURL(path: string) { - const authStateInternal = useAuthState()._internal +// Slimmed down type to allow easy unit testing +interface RuntimeConfig { + public: { + auth: { + baseURL: string + disableInternalRouting: boolean + originEnvKey: string + } + } +} - let baseUrl = authStateInternal.pathname - if (authStateInternal.origin) { - baseUrl = joinURL(authStateInternal.origin, authStateInternal.pathname) +// TODO Comment below: remove/rewrite/add to docs +// Prios in runtime (baseURL): +// - env variable (using `originEnvKey`); +// - static runtime config (`baseURL` captured during build); +// - defaults; +export function resolveApiUrlPath( + endpointPath: string, + runtimeConfig: RuntimeConfig +): string { + // Fully-specified endpoint path - do not join with `baseURL` + if (endpointPath.startsWith('http://') || endpointPath.startsWith('https://')) { + return endpointPath } - return joinURL(baseUrl, path) + // When internal routing is enabled, drop everything except path + const onlyPathname = !runtimeConfig.public.auth.disableInternalRouting + + const baseURL = resolveApiBaseURL(runtimeConfig, onlyPathname) + return joinURL(baseURL, endpointPath) } -/** - * 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. - * - * More specifically, we need this function to correctly handle the following cases: - * 1. On the client-side, returning `navigateTo(signInUrl)` leads to a `404` error as the next-auth-signin-page was not registered with the vue-router that is used for routing under the hood. For this reason we need to - * 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 - * - * @param href HREF / URL to navigate to - */ -export function navigateToAuthPages(href: string) { - const nuxtApp = useNuxtApp() +export function resolveApiBaseURL(runtimeConfig: RuntimeConfig, returnOnlyPathname: boolean): string { + const authRuntimeConfig = runtimeConfig.public.auth - if (import.meta.server) { - if (nuxtApp.ssrContext) { - // 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() - nuxtApp.ssrContext!._renderResponse = { - statusCode: sanitizeStatusCode(302, 302), - body: ``, - headers: { location: encodedHeader }, - } - abortNavigation() - }) + // Default to static runtime config (still overridable using `NUXT_PUBLIC_AUTH_BASE_URL`) + let baseURL = authRuntimeConfig.baseURL + + // Override base URL using environment variable specified in `originEnvKey` if any. + // By default, would use `AUTH_ORIGIN`, can be changed by user + if (authRuntimeConfig.originEnvKey) { + const envBaseURL = process.env[authRuntimeConfig.originEnvKey] + if (envBaseURL) { + baseURL = envBaseURL } } - window.location.href = href - // If href contains a hash, the browser does not reload the page. We reload manually. - if (href.includes('#')) { - window.location.reload() + if (returnOnlyPathname) { + baseURL = parsePath(baseURL).pathname } - // 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 } + return baseURL +} - // 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 +/** Slimmed down auth runtime config for `determineCallbackUrl` */ +interface AuthRuntimeConfigForCallbackUrl { + globalAppMiddleware: { + addDefaultCallbackUrl?: string | boolean + } | boolean } /** - * Determins the desired callback url based on the users desires. Either: + * Determines the desired callback url based on the users desires. Either: * - uses a hardcoded path the user provided, * - determines the callback based on the target the user wanted to reach * * @param authConfig Authentication runtime module config * @param getOriginalTargetPath Function that returns the original location the user wanted to reach */ -export function determineCallbackUrl>(authConfig: ModuleOptionsNormalized, getOriginalTargetPath: () => T): T | string | undefined { +export function determineCallbackUrl>( + authConfig: AuthRuntimeConfigForCallbackUrl, + getOriginalTargetPath: () => T +): T | string | undefined { const authConfigCallbackUrl = typeof authConfig.globalAppMiddleware === 'object' ? authConfig.globalAppMiddleware.addDefaultCallbackUrl : undefined diff --git a/tests/local.url.spec.ts b/tests/local.url.spec.ts index fc9ba33d..4842f944 100644 --- a/tests/local.url.spec.ts +++ b/tests/local.url.spec.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from 'vitest' -import { resolveApiUrlPath } from '../src/runtime/composables/local/utils/url' +import { resolveApiUrlPath } from '../src/runtime/utils/url' describe('endpoint path construction', () => { describe('relative baseURL', () => { @@ -184,6 +184,7 @@ function mockRuntimeConfig(desiredBaseURL: string, envVariableName: string) { public: { auth: { baseURL: desiredBaseURL, + disableInternalRouting: true, originEnvKey: envVariableName } }