Passport
Middleware & Route Protection
Protecting routes and managing authentication flows with middleware
Learn how to protect routes and manage authentication flows using Nuxt Umbu middleware with Laravel Passport.
Overview
Nuxt Umbu provides built-in middleware for protecting routes and managing authentication flows. These middleware automatically handle token validation, user authentication, and redirect logic.
Built-in Middleware
Auth Middleware
The auth middleware protects routes by requiring user authentication:
middleware/auth.ts
// This file is automatically provided by Nuxt Umbu
export default defineNuxtRouteMiddleware(async (to) => {
const { loggedIn, fetchProfile } = useAuth()
if (!loggedIn.value) {
// Redirect to login page
return navigateTo('/login')
}
try {
// Verify token is still valid
await fetchProfile('client')
} catch (error) {
// Token invalid, redirect to login
return navigateTo('/login')
}
})
Two-Factor Auth Middleware
The twoFactor middleware ensures 2FA verification when required:
middleware/twoFactor.ts
// This file is automatically provided by Nuxt Umbu
export default defineNuxtRouteMiddleware(async (to) => {
const { loggedIn, user } = useAuth()
if (!loggedIn.value) {
return navigateTo('/login')
}
// Check if user has 2FA enabled but not verified
if (user.value?.two_factor_enabled && !user.value?.two_factor_verified) {
return navigateTo('/2fa/verify')
}
})
Using Middleware
Page-Level Protection
Protect individual pages using the definePageMeta macro:
pages/dashboard.vue
<template>
<div>
<h1>Dashboard</h1>
<p>Welcome, {{ user?.name }}!</p>
</div>
</template>
<script setup>
definePageMeta({
middleware: 'umbu:auth' // Requires authentication
})
const { user } = useAuth()
</script>
Route-Level Protection
Configure middleware in your route definitions:
pages/admin/index.vue
<script setup>
definePageMeta({
middleware: ['umbu:auth', 'umbu:two-factor'] // Multiple middleware
})
</script>
Layout-Level Protection
Apply middleware to entire layouts:
layouts/default.vue
<template>
<div>
<NuxtPage />
</div>
</template>
<script setup>
definePageMeta({
middleware: 'auth' // Apply to all pages using this layout
})
</script>
Custom Middleware
Role-Based Access Control
Create custom middleware for role-based protection:
middleware/admin.ts
export default defineNuxtRouteMiddleware(async (to) => {
const { loggedIn, user } = useAuth()
if (!loggedIn.value) {
return navigateTo('/login')
}
// Check if user has admin role
if (!user.value?.roles?.includes('admin')) {
throw createError({
statusCode: 403,
statusMessage: 'Access denied'
})
}
})
Permission-Based Access
middleware/permission.ts
export default defineNuxtRouteMiddleware((to) => {
const { loggedIn, user } = useAuth()
const requiredPermission = to.meta.permission
if (!loggedIn.value) {
return navigateTo('/login')
}
if (requiredPermission && !user.value?.permissions?.includes(requiredPermission)) {
throw createError({
statusCode: 403,
statusMessage: 'Insufficient permissions'
})
}
})
Subscription-Based Access
middleware/subscription.ts
export default defineNuxtRouteMiddleware(async (to) => {
const { loggedIn, user } = useAuth()
const requiredPlan = to.meta.plan
if (!loggedIn.value) {
return navigateTo('/login')
}
if (requiredPlan) {
const userPlan = user.value?.subscription?.plan
if (!userPlan || userPlan !== requiredPlan) {
return navigateTo('/billing/upgrade')
}
}
})
Advanced Middleware Patterns
Conditional Middleware
middleware/conditional.ts
export default defineNuxtRouteMiddleware(async (to) => {
const { loggedIn, user } = useAuth()
// Only apply to certain routes
if (!to.path.startsWith('/admin')) {
return
}
if (!loggedIn.value) {
return navigateTo('/login')
}
// Additional admin checks
if (!user.value?.is_admin) {
throw createError({
statusCode: 403,
statusMessage: 'Admin access required'
})
}
})
Multi-Strategy Authentication
middleware/multi-strategy.ts
export default defineNuxtRouteMiddleware(async (to) => {
const { loggedIn, strategy } = useAuth()
const requiredStrategy = to.meta.strategy
if (!loggedIn.value) {
return navigateTo('/login')
}
if (requiredStrategy && strategy !== requiredStrategy) {
// Redirect to login with specific strategy
return navigateTo(`/login?strategy=${requiredStrategy}`)
}
})
API Route Protection
server/api/protected/[...].ts
import protectedMiddleware from '#auth-utils/server/middleware/protected'
import { getAuthSession } from '#auth-utils/server/utils'
export default defineEventHandler(async (event) => {
// Use Nuxt Umbu's protected middleware for authentication
await protectedMiddleware(event)
// Get authentication session data
const { token, strategyName } = getAuthSession(event)
// User is authenticated, proceed with API logic
return { data: 'Protected data', strategy: strategyName }
})
Route Configuration
Dynamic Route Protection
pages/[id]/edit.vue
<script setup>
definePageMeta({
middleware: async (to) => {
const { loggedIn, user } = useAuth()
const resourceId = to.params.id
if (!loggedIn.value) {
return navigateTo('/login')
}
// Check if user owns this resource
try {
const resource = await $fetch(`/api/resources/${resourceId}`)
if (resource.user_id !== user.value?.id) {
throw createError({
statusCode: 403,
statusMessage: 'Access denied'
})
}
} catch (error) {
if (error.statusCode === 404) {
throw createError({
statusCode: 404,
statusMessage: 'Resource not found'
})
}
throw error
}
}
})
</script>
Public Route with Optional Auth
pages/profile/[username].vue
<script setup>
definePageMeta({
middleware: async (to) => {
const { loggedIn, fetchProfile } = useAuth()
// Try to authenticate, but don't require it
if (loggedIn.value) {
try {
await fetchProfile('client')
} catch (error) {
// Silently fail, user remains unauthenticated
console.warn('Auth failed:', error)
}
}
}
})
</script>
Middleware Configuration
Global Middleware Setup
nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-umbu'],
auth: {
provider: 'passport',
// Global middleware configuration
globalMiddleware: {
auth: ['auth'], // Apply to all routes
guest: ['guest'] // Apply to guest routes
}
}
})
Custom Middleware Registration
plugins/middleware.ts
export default defineNuxtPlugin((nuxtApp) => {
// Register custom middleware
addRouteMiddleware('custom-auth', async (to) => {
const { loggedIn } = useAuth()
if (!loggedIn.value && to.path !== '/login') {
return navigateTo('/login')
}
}, { global: true })
})
Error Handling
Custom Error Pages
pages/error.vue
<template>
<div class="error-page">
<h1>{{ error?.statusCode }}</h1>
<p>{{ error?.statusMessage }}</p>
<div v-if="error?.statusCode === 401">
<p>Please log in to access this page.</p>
<NuxtLink to="/login">Go to Login</NuxtLink>
</div>
<div v-else-if="error?.statusCode === 403">
<p>You don't have permission to access this page.</p>
<NuxtLink to="/dashboard">Go to Dashboard</NuxtLink>
</div>
<div v-else>
<p>Something went wrong.</p>
<NuxtLink to="/">Go Home</NuxtLink>
</div>
</div>
</template>
<script setup>
const error = useError()
</script>
Middleware Error Handling
middleware/error-handling.ts
export default defineNuxtRouteMiddleware(async (to) => {
try {
const { loggedIn, fetchProfile } = useAuth()
if (!loggedIn.value) {
return navigateTo('/login')
}
await fetchProfile('client')
} catch (error) {
console.error('Middleware error:', error)
// Clear invalid auth data
const { logout } = useAuth()
await logout('client')
// Redirect with error message
return navigateTo({
path: '/login',
query: { error: 'session_expired' }
})
}
})
Performance Optimization
Middleware Caching
middleware/cached-auth.ts
export default defineNuxtRouteMiddleware(async (to) => {
const { loggedIn } = useAuth()
return loggedIn.value ? undefined : navigateTo('/login')
})
Lazy Loading Middleware
middleware/lazy-auth.ts
export default defineNuxtRouteMiddleware(async (to) => {
// Only run auth check for protected routes
if (to.meta.public) {
return
}
const { loggedIn } = useAuth()
if (!loggedIn.value) {
return navigateTo('/login')
}
})
Testing Middleware
Unit Testing
tests/middleware/auth.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest'
describe('Auth Middleware', () => {
beforeEach(() => {
// Mock Nuxt runtime and useAuth
vi.mock('#imports', () => ({
useAuth: () => ({
loggedIn: { value: false },
fetchProfile: vi.fn()
})
}))
})
it('redirects unauthenticated users', async () => {
const { useAuth } = await import('#imports')
const { loggedIn } = useAuth()
const result = await global.$nuxt.$middleware.umbu.auth({
path: '/protected',
meta: {}
})
expect(result?.path).toBe('/login')
})
it('allows authenticated users', async () => {
const { useAuth } = await import('#imports')
const { loggedIn } = useAuth()
loggedIn.value = true
const result = await global.$nuxt.$middleware.umbu.auth({
path: '/protected',
meta: {}
})
expect(result).toBeUndefined()
})
})
Next: Explore Examples for practical implementations and use cases.