Middleware
This document describes the authentication middleware system that protects routes and manages user access control throughout the application.
Overview
The authentication middleware is a critical security layer that automatically validates user authentication status for every route navigation. It implements a comprehensive protection system that handles token validation, automatic refresh, site-type specific access control, and intelligent redirection logic.
The middleware operates both on server-side and client-side, ensuring consistent authentication enforcement across the entire application lifecycle. It integrates seamlessly with the authentication composable and user store to provide a unified security experience.
Middleware Architecture
Global Authentication Middleware
graph TD A[Route Navigation] --> B[auth.global.ts] B --> C[Extract Cookies] C --> D[Decode JWT Token] D --> E{Token Valid?} E -->|Yes| F[Set Authenticated State] E -->|No| G[Check Refresh Token] G --> H{Refresh Valid?} H -->|Yes| I[Call Refresh API] H -->|No| J[Set Unauthenticated] I --> K{Refresh Success?} K -->|Yes| L[Update Tokens] K -->|No| J F --> M[Check Site Type] J --> M L --> M M --> N{Site Private?} N -->|Yes| O[Private Site Logic] N -->|No| P[Public Site Logic] O --> Q[Allow/Redirect] P --> Q Q --> R[Continue Navigation]
The global middleware (middleware/auth.global.ts
) runs on every route change and provides comprehensive authentication checking with automatic token refresh and site-type aware access control.
Middleware Components
Component | Type | Purpose | Scope |
---|---|---|---|
auth.global.ts | Global | Route protection and token validation | All routes |
checkout.ts | Named | Cart status validation for checkout | Checkout flow |
impersonate.ts | Named | User impersonation protection | Impersonation routes |
server/middleware/auth.ts | Server | Server-side request processing | API requests |
Core Functionality
1. Token Validation and Management
// JWT Token Decoding and Validation
function decodeJwt(token: string): any | null {
try {
const parts = token.split(".");
if (parts.length !== 3) return null;
const payload = parts[1];
let decodedPayload: string;
// Universal decoding (server and client)
if (process.server) {
decodedPayload = Buffer.from(payload, "base64").toString("utf8");
} else {
decodedPayload = atob(payload);
}
return JSON.parse(decodedPayload);
} catch (error) {
console.error("JWT decoding error:", error);
return null;
}
}
// Token Validation Logic
const validateJwt = (token: string) => {
if (!token) return false;
const decoded = decodeJwt(token);
return decoded?.exp && Date.now() / 1000 < decoded.exp;
};
2. Automatic Token Refresh
// Automatic Token Refresh System
async function checkAuthOnServer() {
// Check if current access token is valid
if (jwtCookie.value && validateJwt(jwtCookie.value)) {
return true;
}
// Validate refresh token before attempting refresh
if (refreshCookie.value && !validateJwt(refreshCookie.value)) {
return false;
}
try {
// Attempt token refresh
const { data } = await useFetch("/api/auth/refresh", {
method: "POST",
body: {
jwt: jwtCookie.value,
refreshToken: refreshCookie.value,
},
});
if (
data.value?.isAuthenticated &&
data.value.token &&
data.value.refreshToken
) {
// Update cookies with new tokens
jwtCookie.value = data.value.token;
refreshCookie.value = data.value.refreshToken;
return true;
}
return false;
} catch (error) {
console.error("Token refresh failed:", error);
return false;
}
}
3. Site Type Management
The middleware supports two operational modes based on the frontMode
configuration:
Private Site Mode
- Default Behavior: All routes require authentication
- Unauthenticated Access: Only authentication pages (
/auth/*
) - Redirect Logic: Unauthenticated users → Login page
- Use Case: Internal business applications, admin panels
Public Site Mode
- Default Behavior: Public access to most content
- Protected Routes: Only routes with
requiresAuth
meta - Redirect Logic: Unauthenticated users → Home page
- Use Case: E-commerce sites, marketing websites
// Site Type Configuration
const config = useRuntimeConfig();
const siteType = config.public.frontMode; // 'private' | 'public'
// Authentication-specific pages
const authPages = [
"/auth/login",
"/auth/register",
"/auth/forgot-password",
"/auth/reset-password",
];
// Redirection Logic
if (siteType === "private") {
// Private site: redirect to login if not authenticated
if (!isAuthenticated && !isAuthPage) {
return navigateTo("/auth/login");
}
} else if (siteType === "public") {
// Public site: check route-specific authentication requirements
if (!isAuthenticated && to.meta.requiresAuth) {
return navigateTo("/");
}
}
Route Protection Patterns
1. Global Protection (auth.global.ts)
export default defineNuxtRouteMiddleware(
async (to: RouteLocationNormalized) => {
// Extract authentication cookies
const jwtCookie = useCookie("token");
const refreshCookie = useCookie("refreshToken");
const userStore = useUserStore();
// Perform authentication check with automatic refresh
const isAuthenticated = await checkAuthOnServer();
// Update store and cookie state
userStore.isAuthenticated = isAuthenticated;
useCookie("isAuthenticated").value = isAuthenticated;
// Apply site-specific protection logic
if (isAuthenticated) {
// Prevent access to auth pages when already authenticated
if (isAuthPage) {
return navigateTo("/");
}
} else {
// Handle unauthenticated access based on site type
if (siteType === "private" && !isAuthPage) {
return navigateTo("/auth/login");
}
if (siteType === "public" && to.meta.requiresAuth) {
return navigateTo("/");
}
}
}
);
2. Specific Route Protection
Checkout Protection
// middleware/checkout.ts
export default defineNuxtRouteMiddleware((to, from) => {
const { getCartStatus } = useCart();
const cartStore = useCartStore();
const cart = computed(() => cartStore.cart);
if (cart.value) {
const cartStatus = computed(() => getCartStatus(cart.value));
// Only allow checkout for ongoing carts (not for quotes)
if (cartStatus.value !== "ON_GOING" && !to.path.includes("/quotes")) {
return navigateTo("/");
}
}
});
Impersonation Protection
// middleware/impersonate.ts
export default defineNuxtRouteMiddleware((to, from) => {
// Prevent direct client-side access to impersonation routes
if (process.client && to.path === "/impersonate") {
window.location.replace("/");
}
});
Server-Side Middleware
API Request Protection
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
// Currently minimal implementation
// Future expansion for API-specific authentication
return;
});
Future Enhancements:
- API endpoint authentication validation
- Request header verification
- Rate limiting integration
- API-specific error handling
Integration with Authentication System
Store Synchronization
// User Store Integration
const userStore = useUserStore();
// Update authentication state
userStore.isAuthenticated = isAuthenticated;
// Sync with cookie for client-side access
useCookie("isAuthenticated").value = isAuthenticated;
Composable Integration
// Integration with useDjustAuth composable
const { ensureAuthenticated } = useDjustAuth();
// Middleware uses same token validation logic
// Ensures consistency across authentication methods
Error Handling and Recovery
Token Refresh Failure
try {
const refreshResult = await useFetch("/api/auth/refresh", {
method: "POST",
body: { jwt: jwtCookie.value, refreshToken: refreshCookie.value },
});
if (refreshResult.data.value?.isAuthenticated) {
// Success: Update tokens and continue
jwtCookie.value = refreshResult.data.value.token;
refreshCookie.value = refreshResult.data.value.refreshToken;
} else {
// Failure: Mark as unauthenticated
userStore.isAuthenticated = false;
}
} catch (error) {
console.error("Authentication check failed:", error);
// Graceful degradation: assume unauthenticated
userStore.isAuthenticated = false;
}
Network Failure Handling
// Graceful handling of network issues during authentication
catch (error) {
console.error('Network error during auth check:', error);
// Don't block navigation on network errors
// Use existing token validity as fallback
return jwtCookie.value && validateJwt(jwtCookie.value);
}
Performance Optimization
Efficient Token Validation
// Client-side validation first (faster)
if (jwtCookie.value && validateJwt(jwtCookie.value)) {
return true; // Skip server round-trip
}
// Server validation only when necessary
// Reduces unnecessary API calls
Smart Refresh Logic
// Only attempt refresh if refresh token is valid
if (refreshCookie.value && !validateJwt(refreshCookie.value)) {
return false; // Skip refresh attempt
}
// Prevents unnecessary API calls with expired refresh tokens
Security Considerations
XSS Protection
- Tokens stored in HTTP-only cookies
- No client-side token exposure
- Secure cookie flags in production
CSRF Protection
- SameSite cookie configuration
- Request origin validation
- Double-submit cookie patterns
Session Security
- Automatic token rotation
- Secure random token generation
- Proper token expiration handling
Configuration Options
Runtime Configuration
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
frontMode: "private", // 'private' | 'public'
// Other auth-related config
},
},
});
Route Meta Configuration
// pages/protected-page.vue
definePageMeta({
requiresAuth: true, // For public sites
middleware: "auth", // Explicit middleware application
});
Testing and Debugging
Development Debugging
// Enhanced logging for development
if (process.env.NODE_ENV === "development") {
console.log("Auth middleware:", {
route: to.path,
authenticated: isAuthenticated,
siteType,
tokenValid: validateJwt(jwtCookie.value),
});
}
Testing Scenarios
- Token Expiration: Verify automatic refresh works
- Invalid Tokens: Ensure proper cleanup and redirect
- Network Failures: Test graceful degradation
- Site Type Changes: Verify behavior in both modes
- Route Protection: Test protected/public route access
Best Practices
1. Middleware Ordering
// Ensure auth middleware runs before route-specific middleware
// Global middleware (auth.global.ts) runs automatically first
2. Error Handling
// Always provide fallback behavior
// Log errors for debugging
// Don't expose sensitive information
3. Performance
// Minimize API calls
// Use efficient token validation
// Implement proper caching strategies
4. Security
// Validate tokens thoroughly
// Handle edge cases securely
// Implement defense in depth
Troubleshooting
Common Issues
- Infinite Redirects: Check site type configuration and route meta
- Token Refresh Loops: Verify refresh token validation logic
- Store Desync: Ensure proper store updates in middleware
- Route Protection Gaps: Verify global middleware coverage
Debug Commands
// Check current authentication state
console.log("User Store:", useUserStore().isAuthenticated);
console.log("Cookie State:", useCookie("isAuthenticated").value);
console.log("Token:", useCookie("token").value);
Updated about 2 months ago