Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: protected route redirect overwrite per page #424

Merged
merged 13 commits into from
Sep 23, 2023
26 changes: 25 additions & 1 deletion docs/content/3.application-side/4.protecting-pages.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Protecting Pages

`nuxt-auth` offers different approaches to protect pages:

1. Global protection: Protects all pages with manual exceptions
2. Local protection: Protects specific pages
3. Custom middleware: Create your own middleware

Briefly summarized, you can enable global protection (1) in your `nuxt.config.ts`:

```ts
export default defineNuxtConfig({
modules: ['@sidebase/nuxt-auth'],
Expand All @@ -18,6 +20,7 @@ export default defineNuxtConfig({
Now *all pages* will require sign-in. Learn how to add excepted pages [below](/nuxt-auth/application-side/protecting-pages#disabling-the-global-middleware-locally)

To enable page-local protection (2), add the following `definePageMeta` directive to a page:

```vue
<!-- file: ~/pages/protected.vue -->
<template>
Expand All @@ -34,6 +37,7 @@ You cannot mix approach (1) and (2). So, if the global middleware is enabled, yo
## Global middleware

To create a global authentication middleware that ensures that your user is authenticated no matter which page they visit, you configure `nuxt-auth` as follows:

```ts
export default defineNuxtConfig({
modules: ['@sidebase/nuxt-auth'],
Expand All @@ -45,9 +49,24 @@ export default defineNuxtConfig({

That's it! Every page of your application will now need authentication for the user to visit it.

### Middleware Options

#### `unauthenticatedOnly`

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

#### `navigateAuthenticatedTo`

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

#### `navigateUnauthenticatedTo`

Where to redirect unauthenticated users if this page is protected

### Disabling the global middleware locally

To disable the global middleware on a specific page only, you can use the [`definePageMeta` macro](https://nuxt.com/docs/api/utils/define-page-meta#definepagemeta) to turn `auth` off:

```vue
<!-- file: ~/pages/index.vue -->
<template>
Expand All @@ -61,10 +80,10 @@ definePageMeta({ auth: false })

Note: This only works on `pages/`. It notably does not work inside the `app.vue`.


## Local middleware

To protect specific pages with a middleware, you can use the [`definePageMeta` macro](https://nuxt.com/docs/api/utils/define-page-meta#definepagemeta) to turn `auth` on:

```vue
<!-- file: ~/pages/unprotected.vue -->
<template>
Expand All @@ -87,15 +106,18 @@ Creating a custom middleware is an advanced, experimental option and may result
::

To implement your custom middleware:

1. Create an application-side middleware that applies either globally or is named (see [the Nuxt docs for more](https://nuxt.com/docs/guide/directory-structure/middleware#middleware-directory))
2. 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](https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529). In order to avoid these problems, you will need to either:

- use the undocumented `callWithNuxt` utility when `await`ing other composables,
- return an async function where possible instead of `await`ing it to avoid `callWithNuxt`

Following are examples of both kinds of usage:
::code-group

```ts [direct return]
// file: ~/middleware/authentication.global.ts
export default defineNuxtRouteMiddleware((to) => {
Expand All @@ -114,6 +136,7 @@ export default defineNuxtRouteMiddleware((to) => {
return signIn(undefined, { callbackUrl: to.path }) as ReturnType<typeof navigateTo>
})
```

```ts [callWithNuxt]
// file: ~/middleware/authentication.global.ts
import { callWithNuxt, useNuxtApp } from '#app'
Expand All @@ -137,6 +160,7 @@ export default defineNuxtRouteMiddleware((to) => {
await callWithNuxt(nuxtApp, signIn, [undefined, { callbackUrl: to.path }])
})
```

::
::alert{type="info"}
Prior to v0.5.0 `useAuth()` was called `useSession()`.
Expand Down
22 changes: 20 additions & 2 deletions src/runtime/middleware/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,23 @@ import { navigateToAuthPages, determineCallbackUrl } from '../utils/url'
import { useAuth } from '#imports'

type MiddlewareMeta = boolean | {
unauthenticatedOnly: true,
/** Whether to only allow unauthenticated users to access this page.
LouisHaftmann marked this conversation as resolved.
Show resolved Hide resolved
*
* Authenticated users will be redirected to `/` or the route defined in `navigateAuthenticatedTo`
*
* @default undefined
*/
unauthenticatedOnly?: boolean,
/** Where to redirect authenticated users if `unauthenticatedOnly` is set to true
*
* @default undefined
*/
navigateAuthenticatedTo?: string,
/** Where to redirect unauthenticated users if this page is protected
*
* @default undefined
*/
navigateUnauthenticatedTo?: string
}

declare module '#app/../pages/runtime/composables' {
Expand Down Expand Up @@ -61,6 +76,9 @@ 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>
} else {
return navigateTo(authConfig.provider.pages.login)
if(typeof metaAuth === 'object' && metaAuth.navigateUnauthenticatedTo)
return navigateTo(metaAuth.navigateUnauthenticatedTo)
else
return navigateTo(authConfig.provider.pages.login)
}
})