import crypto from 'crypto'; /** * Server-side CSRF token management * Uses Next.js server APIs - only call from server components/actions */ import { cookies } from "next/headers"; import { generateCsrfToken } from "./csrf-utils"; /** * Set CSRF token in cookies (call during form render) * @param token CSRF token to store */ export async function setCsrfTokenCookie(token: string): Promise { const cookieStore = await cookies(); cookieStore.set("csrf-token", token, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "strict", maxAge: 3600, // 1 hour path: "/", }); } /** * Validate CSRF token from request * @param token Token from form submission * @returns true if token is valid */ export async function validateCsrfToken(token: string | null | undefined): Promise { if (!token || typeof token !== "string") { return false; } try { const cookieStore = await cookies(); const storedToken = cookieStore.get("csrf-token")?.value; if (!storedToken) { return false; } // Timing-safe comparison to prevent timing attacks return crypto.timingSafeEqual( Buffer.from(token), Buffer.from(storedToken) ) as unknown as boolean; } catch { return false; } } /** * Get or create CSRF token for the current request * Use this in server components to ensure token exists */ export async function ensureCsrfToken(): Promise { const cookieStore = await cookies(); let token = cookieStore.get("csrf-token")?.value; if (!token) { token = generateCsrfToken(); await setCsrfTokenCookie(token); } return token; }