From b1e4abad275c020daf8e227c1b4c9c7d1e485465 Mon Sep 17 00:00:00 2001 From: Thomas <36050256+Thomas-Philippot@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:16:18 +0100 Subject: [PATCH] enh(#935): allow external login page (#936) * allow external link as login page * allow external login page * fix typecheck * chore: adjust the external handling --------- Co-authored-by: Thomas Co-authored-by: Zoey Co-authored-by: Marsel Shaikhin --- src/runtime/middleware/sidebase-auth.ts | 18 +++++++++++++----- src/runtime/utils/fetch.ts | 3 +-- src/runtime/utils/logger.ts | 1 + src/runtime/utils/url.ts | 11 ++++++++++- 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 src/runtime/utils/logger.ts diff --git a/src/runtime/middleware/sidebase-auth.ts b/src/runtime/middleware/sidebase-auth.ts index 094ed2d1..76409d69 100644 --- a/src/runtime/middleware/sidebase-auth.ts +++ b/src/runtime/middleware/sidebase-auth.ts @@ -1,5 +1,6 @@ -import { determineCallbackUrl } from '../utils/url' +import { determineCallbackUrl, isExternalUrl } from '../utils/url' import { isProduction } from '../helpers' +import { ERROR_PREFIX } from '../utils/logger' import { defineNuxtRouteMiddleware, navigateTo, useAuth, useRuntimeConfig } from '#imports' type MiddlewareMeta = boolean | { @@ -98,7 +99,14 @@ export default defineNuxtRouteMiddleware((to) => { return navigateTo(options.navigateUnauthenticatedTo) } + const loginPage = authConfig.provider.pages.login + if (typeof loginPage !== 'string') { + console.warn(`${ERROR_PREFIX} provider.pages.login is misconfigured`) + return + } + // Default callback URL was provided + const external = isExternalUrl(loginPage) if (typeof globalAppMiddleware === 'object' && globalAppMiddleware.addDefaultCallbackUrl) { let redirectUrl: string = to.fullPath if (typeof globalAppMiddleware.addDefaultCallbackUrl === 'string') { @@ -106,15 +114,15 @@ export default defineNuxtRouteMiddleware((to) => { } return navigateTo({ - path: authConfig.provider.pages.login, + path: loginPage, query: { redirect: redirectUrl } - }) + }, { external }) } // Fall back to login page - return navigateTo(authConfig.provider.pages.login) + return navigateTo(loginPage, { external }) }) interface MiddlewareOptionsNormalized { @@ -146,7 +154,7 @@ function normalizeUserOptions(userOptions: MiddlewareMeta | undefined): Middlewa if (userOptions.unauthenticatedOnly === undefined) { if (!isProduction) { console.warn( - '[@sidebase/nuxt-auth] `unauthenticatedOnly` was not provided to `definePageMeta` - defaulting to Guest Mode enabled. ' + `${ERROR_PREFIX} \`unauthenticatedOnly\` was not provided to \`definePageMeta\` - defaulting to Guest Mode enabled. ` + 'Read more at https://auth.sidebase.io/guide/application-side/protecting-pages#middleware-options' ) } diff --git a/src/runtime/utils/fetch.ts b/src/runtime/utils/fetch.ts index cd92644f..f1eb8902 100644 --- a/src/runtime/utils/fetch.ts +++ b/src/runtime/utils/fetch.ts @@ -1,9 +1,8 @@ import { resolveApiUrlPath } from './url' +import { ERROR_PREFIX } from './logger' 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) diff --git a/src/runtime/utils/logger.ts b/src/runtime/utils/logger.ts new file mode 100644 index 00000000..2a804260 --- /dev/null +++ b/src/runtime/utils/logger.ts @@ -0,0 +1 @@ +export const ERROR_PREFIX = '[@sidebase/nuxt-auth]' diff --git a/src/runtime/utils/url.ts b/src/runtime/utils/url.ts index a98517a6..847a3d73 100644 --- a/src/runtime/utils/url.ts +++ b/src/runtime/utils/url.ts @@ -17,7 +17,7 @@ export function resolveApiUrlPath( runtimeConfig: RuntimeConfig ): string { // Fully-specified endpoint path - do not join with `baseURL` - if (endpointPath.startsWith('http://') || endpointPath.startsWith('https://')) { + if (isExternalUrl(endpointPath)) { return endpointPath } @@ -95,3 +95,12 @@ export function determineCallbackUrl>( return getOriginalTargetPath() } } + +/** + * Naively checks if a URL is external or not by comparing against its protocol. + * + * URL being valid is not a concern for this function as it is used with developer-controlled inputs. + */ +export function isExternalUrl(url: string): boolean { + return url.startsWith('http://') || url.startsWith('https://') +}