import { auth } from "@/auth" import { NextResponse } from "next/server" import type { NextRequest } from "next/server" /** * Middleware for session validation, route protection, and security headers * * This middleware: * - Validates session tokens on protected routes * - Redirects unauthenticated users to login * - Handles session expiration * - Allows access to auth pages for unauthenticated users * - Adds security headers (CSRF protection, XSS protection, etc.) * * Requirements: 3.3, 3.5, 9.4 (CSRF protection), 9.5 (security headers) */ export default auth((req) => { const { nextUrl } = req const isLoggedIn = !!req.auth?.user // Define protected routes const isProtectedRoute = nextUrl.pathname === "/" || nextUrl.pathname.startsWith("/list") || nextUrl.pathname.startsWith("/profile") || nextUrl.pathname.startsWith("/invitations") // Define auth pages (login, register, etc.) const isAuthPage = nextUrl.pathname.startsWith("/auth") // Create response let response: NextResponse // Allow access to auth pages regardless of authentication status if (isAuthPage) { // If already logged in and trying to access login/register, redirect to home if (isLoggedIn && (nextUrl.pathname === "/auth/login" || nextUrl.pathname === "/auth/register")) { response = NextResponse.redirect(new URL("/", req.url)) } else { response = NextResponse.next() } } // Redirect to login if accessing protected route without authentication else if (isProtectedRoute && !isLoggedIn) { const loginUrl = new URL("/auth/login", req.url) // Add callback URL to redirect back after login loginUrl.searchParams.set("callbackUrl", nextUrl.pathname) response = NextResponse.redirect(loginUrl) } // Allow access to public routes else { response = NextResponse.next() } // Add security headers - Requirement 9.4, 9.5 const headers = response.headers // CSRF Protection - SameSite cookie attribute is set in auth.ts // This header provides additional CSRF protection headers.set('X-Content-Type-Options', 'nosniff') // XSS Protection headers.set('X-XSS-Protection', '1; mode=block') // Prevent clickjacking attacks headers.set('X-Frame-Options', 'DENY') // Referrer Policy - don't leak referrer information headers.set('Referrer-Policy', 'strict-origin-when-cross-origin') // Content Security Policy - helps prevent XSS attacks // Note: This is a basic CSP. You may need to adjust based on your needs const csp = [ "default-src 'self'", "script-src 'self' 'unsafe-inline' 'unsafe-eval'", // unsafe-inline and unsafe-eval needed for Next.js "style-src 'self' 'unsafe-inline'", // unsafe-inline needed for Tailwind "img-src 'self' data: blob:", "font-src 'self' data:", "connect-src 'self'", "frame-ancestors 'none'", ].join('; ') headers.set('Content-Security-Policy', csp) // Permissions Policy - restrict access to browser features headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()') return response }) /** * Matcher configuration for middleware * Applies middleware to all routes except: * - API routes (handled separately) * - Static files (_next/static) * - Image optimization (_next/image) * - Favicon and other public assets */ export const config = { matcher: [ /* * Match all request paths except for the ones starting with: * - api (API routes) * - _next/static (static files) * - _next/image (image optimization files) * - favicon.ico (favicon file) * - public assets (images, icons, manifest, sw.js) */ "/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|json|js)$).*)", ], }