import 'server-only'; import { cookies } from 'next/headers'; import { createHash, timingSafeEqual } from 'node:crypto'; export const ADMIN_COOKIE = 'ci_admin'; function expected(): string | null { const t = process.env.ADMIN_TOKEN; return t && t.length > 0 ? t : null; } /** Cookie value = sha256(token) so the raw token never sits in the browser. */ export function cookieValueFor(token: string): string { return createHash('sha256').update(token).digest('hex'); } function safeEqual(a: string, b: string): boolean { const ab = Buffer.from(a); const bb = Buffer.from(b); return ab.length === bb.length && timingSafeEqual(ab, bb); } export function tokenMatches(token: string | null | undefined): boolean { const exp = expected(); return !!exp && !!token && safeEqual(token, exp); } /** True when the request carries a valid admin cookie (ยง140-141). */ export async function isAdmin(): Promise { const exp = expected(); if (!exp) return false; const c = (await cookies()).get(ADMIN_COOKIE)?.value; return !!c && safeEqual(c, cookieValueFor(exp)); } export function adminConfigured(): boolean { return expected() != null; }