---
title: "Protected page"
description: "Guard a route with the auth middleware and send signed-in visitors back where they were going."
canonical_url: "https://nuxt-convex-module.dev/recipes/protected-page"
---
# Protected page

> Guard a route with the auth middleware and send signed-in visitors back where they were going.

Two pieces: the `convex-auth` route middleware on the page, and a login page that returns the visitor to the page they asked for. Needs the [Better Auth](https://nuxt-convex-module.dev/components/better-auth) integration.

```vue [pages/dashboard.vue]
<script setup lang="ts">
import { api } from '#convex/api'

definePageMeta({ middleware: 'convex-auth' })

const me = useQuery(api.users.me)
</script>

<template>
  <h1>Hello, {{ me?.name }}</h1>
</template>
```

An unauthenticated visitor is redirected to `loginPath` (`/login` by default) with `?redirect=/dashboard`. Send them on with `resolveAuthRedirect()` — never with the raw query:

```vue [pages/login.vue]
<script setup lang="ts">
const route = useRoute()
const { client } = useBetterAuth()
const email = ref('')
const password = ref('')

async function onSubmit() {
  await client.signIn.email({ email: email.value, password: password.value })
  await navigateTo(resolveAuthRedirect(route.query.redirect))
}
</script>

<template>
  <form @submit.prevent="onSubmit">
    <input v-model="email" type="email">
    <input v-model="password" type="password">
    <button>Sign in</button>
  </form>
</template>
```

- `resolveAuthRedirect` returns the destination only for a same-origin path; anything else falls back to `/` (or the second argument), so the login page can't be turned into an open redirect.
- For a section that should render *something* to everyone, skip the middleware and use `<Authenticated>` / `<Unauthenticated>` around the private part — [Auth state](https://nuxt-convex-module.dev/guide/auth-state).
- The middleware runs on the server too, so the redirect happens before any HTML is sent; authenticated responses carry `Cache-Control: private, no-store` — [Security](https://nuxt-convex-module.dev/getting-started/security#auth-tokens-in-ssr-payloads).

`loginPath`, the middleware name and `crossDomainCallbackRoute`: [Configuration](https://nuxt-convex-module.dev/getting-started/configuration#module-options-convex).


## Sitemap

See the full [sitemap](https://nuxt-convex-module.dev/sitemap.md) for all pages.
