auto-claude: subtask-1-1 - Create server-side CSRF token generation and valid

This commit is contained in:
2026-01-25 06:31:45 +01:00
parent eec1758827
commit b1e89b64c5
+94
View File
@@ -0,0 +1,94 @@
import { cookies } from 'next/headers';
import { randomBytes } from 'crypto';
const CSRF_TOKEN_COOKIE_NAME = 'csrf_token';
const CSRF_TOKEN_LENGTH = 32;
/**
* Generate a cryptographically secure CSRF token
*/
export function generateCsrfToken(): string {
return randomBytes(CSRF_TOKEN_LENGTH).toString('base64url');
}
/**
* Get the current CSRF token from cookies or generate a new one
*/
export async function getCsrfToken(): Promise<string> {
const cookieStore = await cookies();
const existingToken = cookieStore.get(CSRF_TOKEN_COOKIE_NAME);
if (existingToken?.value) {
return existingToken.value;
}
const newToken = generateCsrfToken();
await setCsrfToken(newToken);
return newToken;
}
/**
* Set the CSRF token in an httpOnly cookie
*/
export async function setCsrfToken(token: string): Promise<void> {
const cookieStore = await cookies();
try {
cookieStore.set(CSRF_TOKEN_COOKIE_NAME, token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 60 * 60 * 24, // 24 hours
path: '/',
});
} catch {
// Handle server component context where cookies can't be set
}
}
/**
* Validate a CSRF token against the stored token in cookies
*/
export async function validateCsrfToken(token: string): Promise<boolean> {
const cookieStore = await cookies();
const storedToken = cookieStore.get(CSRF_TOKEN_COOKIE_NAME);
if (!storedToken?.value || !token) {
return false;
}
// Constant-time comparison to prevent timing attacks
return timingSafeEqual(
Buffer.from(storedToken.value),
Buffer.from(token)
);
}
/**
* Timing-safe string comparison to prevent timing attacks
*/
function timingSafeEqual(a: Buffer, b: Buffer): boolean {
if (a.length !== b.length) {
return false;
}
let result = 0;
for (let i = 0; i < a.length; i++) {
result |= a[i] ^ b[i];
}
return result === 0;
}
/**
* Delete the CSRF token cookie
*/
export async function deleteCsrfToken(): Promise<void> {
const cookieStore = await cookies();
try {
cookieStore.delete(CSRF_TOKEN_COOKIE_NAME);
} catch {
// Handle server component context
}
}