- Add rate limiting for login, registration, and invitations - Implement CSRF protection with SameSite cookies and security headers - Configure secure session cookies with httpOnly and secure flags - Enhance input sanitization for XSS and injection prevention - Add comprehensive security tests (41 tests passing) - Document security enhancements in SECURITY_ENHANCEMENTS.md Requirements: 9.3, 9.4, 9.5
111 lines
3.7 KiB
TypeScript
111 lines
3.7 KiB
TypeScript
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)$).*)",
|
|
],
|
|
}
|