Skip to main content

Route protection

Redirect anonymous navigation while preserving backend authorization as the security boundary.

Route protection waits until the auth state is known, then redirects anonymous users. It controls navigation only. It does not protect Convex data.

Configure defaults

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@lupinum/better-convex-nuxt'],
  convex: {
    auth: {
      origin: process.env.SITE_URL ?? 'http://localhost:3000',
      redirectTo: '/auth/sign-in',
    },
  },
})

Protect a page

ts
definePageMeta({
  convexAuth: true,
})

For a page-specific destination:

ts
definePageMeta({
  convexAuth: { redirectTo: '/account/login' },
})

Only local redirect paths are accepted. Protocol-relative paths, backslashes, control characters, and unsafe decoded paths are rejected. An unsafe page-specific redirectTo falls back to convex.auth.redirectTo; the page stays protected.

Keep signed-in users off guest pages

Mark a sign-in or sign-up page as guest-only:

app/pages/auth/sign-in.vue
definePageMeta({
  convexAuth: 'guest',
})

A signed-out visitor sees the page. A signed-in visitor goes to the validated redirect query path, or else to convex.auth.guestRedirectTo (default /). The middleware never redirects to the current page, and it ignores a repeated redirect parameter.

Preserve the return path

The middleware always appends a validated local redirect query value. After sign-in, read it with useConvexAuthReturnTo(). It returns the normalized path, or null when the value is missing, repeated, or unsafe:

app/pages/auth/sign-in.vue
const returnTo = useConvexAuthReturnTo()

async function afterSignIn() {
  await navigateTo(returnTo.value ?? '/dashboard')
}

To validate another value, call normalizeLocalRedirectPath(value). It accepts any input and returns a safe local path or null. Never navigate to an external URL from a query parameter.

Public pages

Pages without convexAuth metadata remain public by default. An authenticated application can combine public auth: 'none' content and private controls on the same page without protecting the route.

To protect every page by default, set routes: 'protected':

nuxt.config.ts
export default defineNuxtConfig({
  convex: {
    auth: {
      origin: process.env.SITE_URL ?? 'http://localhost:3000',
      redirectTo: '/auth/sign-in',
      routes: 'protected',
    },
  },
})

Then a page without metadata requires sign-in. Opt a public page out with definePageMeta({ convexAuth: false }). The sign-in page never redirects to itself.

Permission checks

Authentication can be resolved in middleware. Product permissions normally require backend data.

Prefer to render the page and query the user's permissions from Convex, unless the navigation itself must be blocked. A permission check in middleware only changes what the user sees. The Convex function must repeat the check.

Caching warning

Do not cache an authenticated SSR page as public HTML. A redirect middleware does not split a shared cache by user.