Skip to content

Route Middleware

Nuxt 3 middleware provides route-level protection and access control throughout the application. The boilerplate includes three middleware types for different security levels.

Middleware Types

Authentication Middleware

Protects routes that require user login.

typescript
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  const user = useSupabaseUser()
  
  const protectedRoutes = ['/dashboard', '/settings', '/billing', '/profile']
  const requiresAuth = protectedRoutes.some(route => to.path.startsWith(route))
  
  if (requiresAuth && !user.value) {
    const redirectTo = to.fullPath
    return navigateTo(`/auth/login?redirect=${encodeURIComponent(redirectTo)}`)
  }
})

Usage in pages:

typescript
definePageMeta({
  middleware: 'auth'
})

Protected routes: Dashboard, settings, billing, profile pages

Admin Middleware

Restricts access to admin-only areas using email-based authorization.

typescript
// middleware/admin.ts
export default defineNuxtRouteMiddleware((to) => {
  const user = useSupabaseUser()
  
  if (!user.value) {
    return navigateTo('/auth/login')
  }
  
  const adminEmails = [
    'milan.herbe@gmail.com',
    'mhdev@flink-agency.dev'
  ]
  
  if (!adminEmails.includes(user.value.email || '')) {
    throw createError({
      statusCode: 403,
      statusMessage: 'Access denied. Admin privileges required.'
    })
  }
})

Usage in pages:

typescript
definePageMeta({
  layout: 'admin',
  middleware: 'admin'
})

Protected routes: All /admin/* pages including user management, analytics, and subscription management

Subscription Middleware

Controls access based on subscription plans and feature availability.

typescript
// middleware/subscription.ts
export default defineNuxtRouteMiddleware(async (to) => {
  const user = useSupabaseUser()
  if (!user.value) return
  
  const requiredPlan = to.meta.requiresPlan as string
  const requiredFeature = to.meta.requiresFeature as string
  
  if (requiredPlan || requiredFeature) {
    const { useSubscription } = await import('@/composables/useSubscription')
    const { checkAccess, getCurrentSubscription } = useSubscription()
    
    const subscription = await getCurrentSubscription()
    
    if (requiredPlan && subscription?.plan?.name !== requiredPlan) {
      return navigateTo('/upgrade?plan=' + requiredPlan)
    }
    
    if (requiredFeature && !checkAccess(requiredFeature)) {
      return navigateTo('/upgrade?feature=' + requiredFeature)
    }
  }
})

Usage in pages:

typescript
definePageMeta({
  middleware: 'subscription',
  requiresPlan: 'pro',
  requiresFeature: 'advanced-analytics'
})

Implementation Details

Route Protection Flow

  1. User visits protected route - Middleware executes before page load
  2. Authentication check - Verifies user session exists
  3. Authorization check - Validates user permissions/subscription
  4. Access decision - Allows access or redirects to appropriate page

Redirect Behavior

  • Unauthenticated users - Redirected to /auth/login with return URL
  • Unauthorized users - Receive 403 error or redirect to upgrade page
  • Subscription required - Redirected to /upgrade with plan/feature context

Page Meta Configuration

Middleware is applied using definePageMeta() in page components:

typescript
// Basic authentication
definePageMeta({
  middleware: 'auth'
})

// Admin access
definePageMeta({
  layout: 'admin',
  middleware: 'admin'
})

// Subscription-based access
definePageMeta({
  middleware: 'subscription',
  requiresPlan: 'enterprise',
  requiresFeature: 'custom-branding'
})

Security Considerations

  • Email-based admin auth - Simple but should be replaced with role-based system in production
  • Client-side checks - Middleware runs on client, server-side validation still required
  • Subscription validation - Real-time checks against current subscription status
  • Graceful redirects - Preserves intended destination for post-login navigation

The middleware system provides layered security from basic authentication to feature-level access control, ensuring users only access content they're authorized to view.

Built with love by mhdevfr