Skip to content

Commit

Permalink
fix(#926): correct and clarify Guest Mode (#929)
Browse files Browse the repository at this point in the history
  • Loading branch information
phoenix-ru authored Oct 17, 2024
1 parent 734953f commit 1aa5e17
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 31 deletions.
8 changes: 5 additions & 3 deletions docs/guide/application-side/protecting-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
98 changes: 70 additions & 28 deletions src/runtime/middleware/auth.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import type { navigateToAuthPages } from '../utils/url'
import { determineCallbackUrl } from '../utils/url'
import { isProduction } from '../helpers'
import { defineNuxtRouteMiddleware, navigateTo, useAuth, useRuntimeConfig } from '#imports'

type MiddlewareMeta = boolean | {
/**
* Whether to only allow unauthenticated users to access this page.
* Whether to allow only unauthenticated users to access this page.
*
* Authenticated users will be redirected to `/` or the route defined in `navigateAuthenticatedTo`
*
* @default undefined
*/
unauthenticatedOnly?: boolean
unauthenticatedOnly: boolean
/**
* Where to redirect authenticated users if `unauthenticatedOnly` is set to true
*
Expand Down Expand Up @@ -38,41 +37,34 @@ declare module 'vue-router' {
}

export default defineNuxtRouteMiddleware((to) => {
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
}
Expand Down Expand Up @@ -101,9 +93,13 @@ 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<typeof navigateToAuthPages>
}
if (typeof metaAuth === 'object' && metaAuth.navigateUnauthenticatedTo) {
return navigateTo(metaAuth.navigateUnauthenticatedTo)

// Redirect path was provided
if (options.navigateUnauthenticatedTo) {
return navigateTo(options.navigateUnauthenticatedTo)
}

// Default callback URL was provided
if (typeof globalAppMiddleware === 'object' && globalAppMiddleware.addDefaultCallbackUrl) {
let redirectUrl: string = to.fullPath
if (typeof globalAppMiddleware.addDefaultCallbackUrl === 'string') {
Expand All @@ -117,5 +113,51 @@ export default defineNuxtRouteMiddleware((to) => {
}
})
}

// Fall back to login page
return navigateTo(authConfig.provider.pages.login)
})

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
}
}
}

0 comments on commit 1aa5e17

Please sign in to comment.