import "server-only"; import { cookies } from "next/headers"; import { sealData, unsealData } from "iron-session"; const COOKIE_NAME = "cm_auth"; const COOKIE_TTL_SECONDS = 30 * 24 * 60 * 60; export type Session = { username: string; authenticatedAt: number; pendingChallenge?: { kind: "register" | "authenticate"; challenge: string; expiresAt: number; }; }; function secret(): string { const s = process.env.CM_AUTH_SECRET; if (!s || s.length < 32) { throw new Error("CM_AUTH_SECRET missing or shorter than 32 chars"); } return s; } export async function getSession(): Promise { const jar = await cookies(); const raw = jar.get(COOKIE_NAME)?.value; if (!raw) return null; try { return await unsealData(raw, { password: secret() }); } catch { return null; } } export async function setSession(session: Session): Promise { const sealed = await sealData(session, { password: secret(), ttl: COOKIE_TTL_SECONDS, }); const jar = await cookies(); jar.set(COOKIE_NAME, sealed, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax", path: "/", maxAge: COOKIE_TTL_SECONDS, }); } export async function clearSession(): Promise { const jar = await cookies(); jar.delete(COOKIE_NAME); } export async function requireSession(): Promise { const s = await getSession(); if (!s) throw new Error("Unauthenticated"); return s; }