import { createHmac, timingSafeEqual } from "crypto"; import { cookies } from "next/headers"; const COOKIE_NAME = "admin_session"; const SESSION_TTL_SECONDS = 60 * 60 * 12; type SessionPayload = { sub: string; exp: number; }; function getSecret() { const secret = process.env.ADMIN_SESSION_SECRET; if (!secret) { throw new Error("ADMIN_SESSION_SECRET is missing."); } return secret; } function encodeBase64Url(value: string) { return Buffer.from(value).toString("base64url"); } function decodeBase64Url(value: string) { return Buffer.from(value, "base64url").toString(); } function sign(value: string) { return createHmac("sha256", getSecret()).update(value).digest("base64url"); } function safeEqual(a: string, b: string) { const aBuffer = Buffer.from(a); const bBuffer = Buffer.from(b); if (aBuffer.length !== bBuffer.length) { return false; } return timingSafeEqual(aBuffer, bBuffer); } export function createAdminToken(username: string) { const payload: SessionPayload = { sub: username, exp: Math.floor(Date.now() / 1000) + SESSION_TTL_SECONDS, }; const encodedPayload = encodeBase64Url(JSON.stringify(payload)); const signature = sign(encodedPayload); return `${encodedPayload}.${signature}`; } function verifyToken(token: string): SessionPayload | null { const [payloadPart, signaturePart] = token.split("."); if (!payloadPart || !signaturePart) { return null; } const expectedSignature = sign(payloadPart); if (!safeEqual(signaturePart, expectedSignature)) { return null; } try { const payload = JSON.parse(decodeBase64Url(payloadPart)) as SessionPayload; if (!payload?.sub || !payload?.exp) { return null; } if (payload.exp < Math.floor(Date.now() / 1000)) { return null; } return payload; } catch { return null; } } export async function isAdminAuthenticated() { const cookieStore = await cookies(); const token = cookieStore.get(COOKIE_NAME)?.value; if (!token) { return false; } return Boolean(verifyToken(token)); } export function getAdminCredentials() { return { username: process.env.ADMIN_USERNAME || "admin", password: process.env.ADMIN_PASSWORD || "change-me", }; } export function getAdminCookieName() { return COOKIE_NAME; }