mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 13:52:16 +01:00
- Fix 36+ biome linting issues reducing errors/warnings from 227 to 191 - Replace explicit 'any' types with proper TypeScript interfaces - Fix React hooks dependencies and useCallback patterns - Resolve unused variables and parameter assignment issues - Improve accessibility with proper label associations - Add comprehensive API documentation for admin and security features - Update README.md with accurate PostgreSQL setup and current tech stack - Create complete documentation for audit logging, CSP monitoring, and batch processing - Fix outdated project information and missing developer workflows
124 lines
2.9 KiB
TypeScript
124 lines
2.9 KiB
TypeScript
/**
|
|
* CSRF Protection Middleware
|
|
*
|
|
* This middleware protects against Cross-Site Request Forgery attacks by:
|
|
* 1. Validating CSRF tokens on state-changing operations
|
|
* 2. Generating new tokens for safe requests
|
|
* 3. Blocking unauthorized requests
|
|
*/
|
|
|
|
import type { NextRequest } from "next/server";
|
|
import { NextResponse } from "next/server";
|
|
import { CSRFProtection, CSRF_CONFIG } from "../lib/csrf";
|
|
|
|
/**
|
|
* Routes that require CSRF protection
|
|
*/
|
|
const PROTECTED_PATHS = [
|
|
// Authentication endpoints
|
|
"/api/auth/signin",
|
|
"/api/auth/signout",
|
|
"/api/register",
|
|
"/api/forgot-password",
|
|
"/api/reset-password",
|
|
|
|
// Dashboard API endpoints
|
|
"/api/dashboard",
|
|
|
|
// Platform admin endpoints
|
|
"/api/platform",
|
|
|
|
// tRPC endpoints (all state-changing operations)
|
|
"/api/trpc",
|
|
] as const;
|
|
|
|
/**
|
|
* HTTP methods that require CSRF protection
|
|
*/
|
|
const PROTECTED_METHODS = ["POST", "PUT", "DELETE", "PATCH"] as const;
|
|
|
|
/**
|
|
* Check if path requires CSRF protection
|
|
*/
|
|
function requiresCSRFProtection(pathname: string, method: string): boolean {
|
|
// Only protect state-changing methods
|
|
if (!PROTECTED_METHODS.includes(method as any)) {
|
|
return false;
|
|
}
|
|
|
|
// Check if path starts with any protected path
|
|
return PROTECTED_PATHS.some((path) => pathname.startsWith(path));
|
|
}
|
|
|
|
/**
|
|
* CSRF protection middleware
|
|
*/
|
|
export async function csrfProtectionMiddleware(
|
|
request: NextRequest
|
|
): Promise<NextResponse> {
|
|
const { pathname } = request.nextUrl;
|
|
const method = request.method;
|
|
|
|
// Skip CSRF protection for safe methods and unprotected paths
|
|
if (!requiresCSRFProtection(pathname, method)) {
|
|
return NextResponse.next();
|
|
}
|
|
|
|
// Validate CSRF token for protected requests
|
|
const validation = await CSRFProtection.validateRequest(request);
|
|
|
|
if (!validation.valid) {
|
|
console.warn(
|
|
`CSRF validation failed for ${method} ${pathname}:`,
|
|
validation.error
|
|
);
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: "Invalid or missing CSRF token",
|
|
code: "CSRF_TOKEN_INVALID",
|
|
},
|
|
{
|
|
status: 403,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
}
|
|
);
|
|
}
|
|
|
|
return NextResponse.next();
|
|
}
|
|
|
|
/**
|
|
* Generate CSRF token endpoint response
|
|
*/
|
|
export function generateCSRFTokenResponse(): NextResponse {
|
|
const { token, cookie } = CSRFProtection.generateTokenResponse();
|
|
|
|
const response = NextResponse.json({
|
|
success: true,
|
|
token,
|
|
});
|
|
|
|
// Set the CSRF token cookie
|
|
response.cookies.set(cookie.name, cookie.value, cookie.options);
|
|
|
|
return response;
|
|
}
|
|
|
|
/**
|
|
* Middleware for serving CSRF tokens to clients
|
|
*/
|
|
export function csrfTokenMiddleware(request: NextRequest): NextResponse | null {
|
|
const { pathname } = request.nextUrl;
|
|
|
|
// Handle CSRF token endpoint
|
|
if (pathname === "/api/csrf-token" && request.method === "GET") {
|
|
return generateCSRFTokenResponse();
|
|
}
|
|
|
|
return null;
|
|
}
|