import { cookies } from 'next/headers'; import { encryptPayload, decryptPayload } from '@/lib/auth/crypto'; import { getCookieOptions } from '@/lib/oauth/cookie-config'; // Step-up re-authentication proof for device pairing. Generating a pairing QR // provisions a new long-lived login on another device, so we require the user // to have re-authenticated at the IdP within a short window first (see the // reauth SSO flow). This proof is a short-lived, encrypted, httpOnly cookie — // it is set only after a fresh IdP login and checked by /api/auth/pair/create. const PAIR_REAUTH_COOKIE = 'pair_reauth'; const PAIR_REAUTH_TTL_MS = 5 * 60 * 1000; // 5 minutes — long enough to render the QR export async function setPairReauth(): Promise { const cookieStore = await cookies(); const value = encryptPayload({ purpose: 'pair', created_at: Date.now() }); cookieStore.set(PAIR_REAUTH_COOKIE, value, { ...getCookieOptions(), maxAge: Math.floor(PAIR_REAUTH_TTL_MS / 1000), }); } export async function hasValidPairReauth(): Promise { const cookieStore = await cookies(); const raw = cookieStore.get(PAIR_REAUTH_COOKIE)?.value; if (!raw) return false; const data = decryptPayload(raw); if (!data || data.purpose !== 'pair') return false; const createdAt = typeof data.created_at === 'number' ? data.created_at : 0; // Belt-and-suspenders alongside the cookie maxAge: a forged/old proof whose // timestamp is outside the window is rejected even if the cookie survived. return Date.now() - createdAt <= PAIR_REAUTH_TTL_MS; } export async function clearPairReauth(): Promise { const cookieStore = await cookies(); cookieStore.delete(PAIR_REAUTH_COOKIE); }