Skip to content

Protecting Pages

NuxtAuth offers different approaches to protect pages:

  • Global middleware: Protects all pages with manual exceptions
  • Local middleware: Protects specific pages
  • Custom middleware: Create your own middleware

Global Middleware

To enable the global middleware on your application, you can configure the middleware inside the nuxt.config.ts.

ts
export default defineNuxtConfig({
  modules: ['@sidebase/nuxt-auth'],
  auth: {
    globalAppMiddleware: true
  }
})

If you'd like to further customize the global middleware, you can pass an object of configurations to globalAppMiddleware. See the API reference here.

When you use the global middleware, but want to disable it on some pages, use the definePageMeta macro to set the authentication metadata for a single page.

vue
<script setup lang="ts">
definePageMeta({
  auth: false // This would disable the middleware for the page
})
</script>

<template>
  I am not protected anymore!
</template>

Local Middleware

When you are not using the global middleware, you can still protect your pages individually using local middleware. To enable or disable the middleware on a single page, use the auth property on definePageMeta.

By setting auth to true, you can automatically enable the middleware on the page:

vue
<script lang="ts" setup>
definePageMeta({
  auth: true
})
</script>

<template>
  I am now protected again!
</template>

INFO

When you use the automatic adding, the middleware will always be added after the other middleware you defined. This means that the following

ts
definePageMeta({
  auth: true,
  middleware: 'other-middleware'
})

will be interpreted as:

ts
definePageMeta({
  auth: true,
  middleware: ['other-middleware', 'sidebase-auth']
})

Manually adding local middleware

Another way of enabling the middleware is by manually specifying it:

vue
<script lang="ts" setup>
definePageMeta({
  middleware: 'sidebase-auth'
})
</script>

This gives you a total control of the order in which the different middlewares are executed:

vue
<script lang="ts" setup>
definePageMeta({
  middleware: ['first-middleware', 'sidebase-auth', 'last-middleware'] 
})
</script>

WARNING

Using local middleware is only available if the global middleware was disabled, as otherwise you will get an error along the lines of Error: Unknown route middleware: 'auth'. This is because the auth middleware is then added globally and not available to use as a local, page-specific middleware.

Middleware options

auth can be either a boolean or an object of further middleware configurations.

vue
<script setup lang="ts">
definePageMeta({
  auth: {
    unauthenticatedOnly: false,
    navigateUnauthenticatedTo: '/auth/signin'
  }
})
</script>

<template>
  I am protected with a custom redirect!
</template>

unauthenticatedOnly

Whether to allow only unauthenticated users to access this page. Authenticated users will be redirected to / or to the route specified in navigateAuthenticatedTo.

If you want to let everyone see the page, set auth: false instead (see Local Middleware).

DANGER

This option is required from 0.9.4 onwards to prevent ambiguity (related issue). Make sure you set it, otherwise Guest Mode will be enabled by default — your guests would be able to see the page, but your authenticated users would be redirected away.

Where to redirect authenticated users if unauthenticatedOnly is set to true.

Where to redirect unauthenticated users if this page is protected.

Guest mode

You can use NuxtAuth to setup pages that are accessible only when the user is not logged in. This is sometimes called "guest mode". The behavior of such a page is as follows:

  • A logged in user visits the page -> redirect to another (likely protected) page,
  • A logged out user visits the page -> they are allowed to stay and view it

This behavior is useful for login pages that you don't want to be visitable by logged in users: Why should they go through a login flow again?

vue
<script setup lang="ts">
definePageMeta({
  auth: {
    unauthenticatedOnly: true,
    navigateAuthenticatedTo: '/profile'
  }
})
</script>

<template>
  I can only be viewed as a guest!
</template>

Custom Middleware

You may create your own application-side middleware in order to implement custom, more advanced authentication logic.

To implement your custom middleware:

  • Create an application-side middleware that applies either globally or is named (see the Nuxt docs for more);
  • Add logic based on useAuth to it.

When adding the logic, you need to watch out when calling other async composable functions. This can lead to context-problems in Nuxt, see the explanation for this here. In order to avoid these problems, you will need to either:

  • use the undocumented callWithNuxt utility when awaiting other composables
  • return an async function where possible instead of awaiting it to avoid callWithNuxt
ts
// file: ~/middleware/authentication.global.ts
export default defineNuxtRouteMiddleware((to) => {
  const { status, signIn } = useAuth()

  // Return immediately if user is already authenticated
  if (status.value === 'authenticated') {
    return
  }

  /**
   * We cannot directly call and/or return `signIn` here as `signIn` uses async composables under the hood, leading to "nuxt instance undefined errors", see https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529
   *
   * So to avoid calling it, we return it immediately.
   */
  return signIn(undefined, { callbackUrl: to.path }) as ReturnType<typeof navigateTo>
})
ts
// file: ~/middleware/authentication.global.ts
import { useNuxtApp } from '#imports'
import { callWithNuxt } from '#app/nuxt'

export default defineNuxtRouteMiddleware((to) => {
  // It's important to do this as early as possible
  const nuxtApp = useNuxtApp()

  const { status, signIn } = useAuth()

  // Return immediately if user is already authenticated
  if (status.value === 'authenticated') {
    return
  }

  /**
   * We cannot directly call and/or return `signIn` here as `signIn` uses async composables under the hood, leading to "nuxt instance undefined errors", see https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529
   *
   * So to avoid calling it, we call it via `callWithNuxt`.
   */
  await callWithNuxt(nuxtApp, signIn, [undefined, { callbackUrl: to.path }])
})

Released under the MIT License.