diff --git a/docs/.vitepress/routes/navbar.ts b/docs/.vitepress/routes/navbar.ts index 84fd95cf..052865df 100644 --- a/docs/.vitepress/routes/navbar.ts +++ b/docs/.vitepress/routes/navbar.ts @@ -44,7 +44,7 @@ export const routes: DefaultTheme.Config['nav'] = [ ], }, { - text: '0.9.3', + text: '0.9.4', items: [ { text: '0.8.2', diff --git a/docs/guide/advanced/deployment/vercel.md b/docs/guide/advanced/deployment/vercel.md index 9ee92a21..744e8dc8 100644 --- a/docs/guide/advanced/deployment/vercel.md +++ b/docs/guide/advanced/deployment/vercel.md @@ -12,7 +12,7 @@ This variable is avalible at both build and run-time. Therefore you can referenc export default defineNuxtConfig({ modules: ['@sidebase/nuxt-auth'], auth: { - baseURL: process.env.VERCEL_URL ? `https://${VERCEL_URL}/api/auth` : undefined + baseURL: process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}/api/auth` : undefined } }) ``` diff --git a/docs/guide/application-side/protecting-pages.md b/docs/guide/application-side/protecting-pages.md index 8d8a7c5e..0df29fd0 100644 --- a/docs/guide/application-side/protecting-pages.md +++ b/docs/guide/application-side/protecting-pages.md @@ -78,10 +78,12 @@ definePageMeta({ #### `unauthenticatedOnly` -Whether to only allow unauthenticated users to access this page. Authenticated users will be redirected to / or the route defined in `navigateAuthenticatedTo`. +Whether to allow only unauthenticated users to access this page. Authenticated users will be redirected to `/` or to the route specified in `navigateAuthenticatedTo`. -:::tip -Setting `unauthenticatedOnly: false` is equivalent to setting `auth: false` from the user perspective, but requires some extra middleware steps, so it is a bit less efficient. Therefore it is recommended to use `auth: false` instead. +If you want to let everyone see the page, set `auth: false` instead (see [Local Middleware](#local-middleware)). + +:::warning +This option is required from `0.9.4` onwards to prevent ambiguity ([related issue](https://github.com/sidebase/nuxt-auth/issues/926)). Make sure you set it, otherwise [Guest Mode](#guest-mode) will be **enabled** by default — your guests would be able to see the page, but your authenticated users would be redirected away. ::: #### `navigateAuthenticatedTo` diff --git a/package.json b/package.json index 56bd80ac..7328de49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sidebase/nuxt-auth", - "version": "0.9.3", + "version": "0.9.4", "license": "MIT", "type": "module", "description": "Authentication built for Nuxt 3! Easily add authentication via OAuth providers, credentials or Email Magic URLs!", diff --git a/playground-authjs/server/api/auth/[...].ts b/playground-authjs/server/api/auth/[...].ts index ede30f11..b4eba584 100644 --- a/playground-authjs/server/api/auth/[...].ts +++ b/playground-authjs/server/api/auth/[...].ts @@ -33,14 +33,13 @@ export default NuxtAuthHandler({ // Any object returned will be saved in `user` property of the JWT return user } - else { - console.error('Warning: Malicious login attempt registered, bad credentials provided') - // If you return null then an error will be displayed advising the user to check their details. - return null + console.error('Warning: Malicious login attempt registered, bad credentials provided') - // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter - } + // If you return null then an error will be displayed advising the user to check their details. + return null + + // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter } }) ] diff --git a/src/runtime/composables/commonAuthState.ts b/src/runtime/composables/commonAuthState.ts index 08b2a006..19c53d67 100644 --- a/src/runtime/composables/commonAuthState.ts +++ b/src/runtime/composables/commonAuthState.ts @@ -24,12 +24,10 @@ export function makeCommonAuthState() { if (loading.value) { return 'loading' } - else if (data.value) { + if (data.value) { return 'authenticated' } - else { - return 'unauthenticated' - } + return 'unauthenticated' }) // Determine base url of app diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index cd4ff21c..1b311dfc 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -152,9 +152,7 @@ async function getSession(getSessionOptions?: GetSessionOptions): Promise { - const metaAuth = typeof to.meta.auth === 'object' - ? { - unauthenticatedOnly: true, - ...to.meta.auth - } - : to.meta.auth - - if (metaAuth === false) { + // Normalize options. If `undefined` was returned, we need to skip middleware + const options = normalizeUserOptions(to.meta.auth) + if (!options) { return } const authConfig = useRuntimeConfig().public.auth const { status, signIn } = useAuth() - const isGuestMode = typeof metaAuth === 'object' && metaAuth.unauthenticatedOnly - // Guest mode happy path 1: Unauthenticated user is allowed to view page + + // Guest Mode - only unauthenticated users are allowed + const isGuestMode = options.unauthenticatedOnly + const isAuthenticated = status.value === 'authenticated' if (isGuestMode && status.value === 'unauthenticated') { + // Guest Mode - unauthenticated users can stay on the page return } - - // Guest mode edge-case: Developer used guest-mode config style but set `unauthenticatedOnly` to `false` - if (typeof metaAuth === 'object' && !metaAuth.unauthenticatedOnly) { - return + else if (isGuestMode && isAuthenticated) { + // Guest Mode - authenticated users should be redirected to another page + return navigateTo(options.navigateAuthenticatedTo) } - - if (status.value === 'authenticated') { - // Guest mode happy path 2: Authenticated user should be directed to another page - if (isGuestMode) { - return navigateTo(metaAuth.navigateAuthenticatedTo ?? '/') - } + else if (isAuthenticated) { + // Authenticated users don't need any further redirects return } // We do not want to block the login page when the local provider is used - if (authConfig.provider?.type === 'local') { - const loginRoute: string | undefined = authConfig.provider?.pages?.login + if (authConfig.provider.type === 'local') { + const loginRoute: string | undefined = authConfig.provider.pages.login if (loginRoute && loginRoute === to.path) { return } @@ -101,23 +93,71 @@ export default defineNuxtRouteMiddleware((to) => { // @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 } - else if (typeof metaAuth === 'object' && metaAuth.navigateUnauthenticatedTo) { - return navigateTo(metaAuth.navigateUnauthenticatedTo) + + // Redirect path was provided + if (options.navigateUnauthenticatedTo) { + return navigateTo(options.navigateUnauthenticatedTo) } - else { - if (typeof globalAppMiddleware === 'object' && globalAppMiddleware.addDefaultCallbackUrl) { - let redirectUrl: string = to.fullPath - if (typeof globalAppMiddleware.addDefaultCallbackUrl === 'string') { - redirectUrl = globalAppMiddleware.addDefaultCallbackUrl + + // Default callback URL was provided + if (typeof globalAppMiddleware === 'object' && globalAppMiddleware.addDefaultCallbackUrl) { + let redirectUrl: string = to.fullPath + if (typeof globalAppMiddleware.addDefaultCallbackUrl === 'string') { + redirectUrl = globalAppMiddleware.addDefaultCallbackUrl + } + + return navigateTo({ + path: authConfig.provider.pages.login, + query: { + redirect: redirectUrl } + }) + } + + // Fall back to login page + return navigateTo(authConfig.provider.pages.login) +}) - return navigateTo({ - path: authConfig.provider.pages.login, - query: { - redirect: redirectUrl +interface MiddlewareOptionsNormalized { + unauthenticatedOnly: boolean + navigateAuthenticatedTo: string + navigateUnauthenticatedTo?: string +} + +/** + * @returns `undefined` is returned when passed options are `false` + */ +function normalizeUserOptions(userOptions: MiddlewareMeta | undefined): MiddlewareOptionsNormalized | undefined { + // false - do not use middleware + // true - use defaults + if (typeof userOptions === 'boolean' || userOptions === undefined) { + return userOptions !== false + ? { + // Guest Mode off if `auth: true` + unauthenticatedOnly: false, + navigateAuthenticatedTo: '/', + navigateUnauthenticatedTo: undefined } - }) + : undefined + } + + // We check in runtime in case usage error was not caught by TS + if (typeof userOptions === 'object') { + // Guest Mode on to preserve compatibility. A warning is also issued to prevent unwanted behaviour + if (userOptions.unauthenticatedOnly === undefined) { + if (!isProduction) { + console.warn( + '[@sidebase/nuxt-auth] `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' + ) + } + userOptions.unauthenticatedOnly = true + } + + return { + unauthenticatedOnly: userOptions.unauthenticatedOnly, + navigateAuthenticatedTo: userOptions.navigateAuthenticatedTo ?? '/', + navigateUnauthenticatedTo: userOptions.navigateUnauthenticatedTo } - return navigateTo(authConfig.provider.pages.login) } -}) +} diff --git a/src/runtime/plugins/refresh-token.server.ts b/src/runtime/plugins/refresh-token.server.ts index bd79d432..1d05065d 100644 --- a/src/runtime/plugins/refresh-token.server.ts +++ b/src/runtime/plugins/refresh-token.server.ts @@ -63,9 +63,7 @@ export default defineNuxtPlugin({ ) return } - else { - rawRefreshToken.value = extractedRefreshToken - } + rawRefreshToken.value = extractedRefreshToken } rawToken.value = extractedToken diff --git a/src/runtime/server/services/authjs/nuxtAuthHandler.ts b/src/runtime/server/services/authjs/nuxtAuthHandler.ts index 56a78704..de3cb797 100644 --- a/src/runtime/server/services/authjs/nuxtAuthHandler.ts +++ b/src/runtime/server/services/authjs/nuxtAuthHandler.ts @@ -194,10 +194,8 @@ function getRequestBaseFromH3Event(event: H3Event, trustHost: boolean): string { return `${protocol}://${host}` } - else { - // This may throw, we don't catch it - const origin = getServerOrigin(event) + // This may throw, we don't catch it + const origin = getServerOrigin(event) - return origin - } + return origin }