import { createHmac, timingSafeEqual } from 'crypto'; import { parseFreshAccessToken, resolveAccessCodeSessionTtlSeconds, type AccessTokenValidationOptions, } from '@/lib/server/access-token-policy'; /** Create an HMAC-signed token: `timestamp.signature` */ export function createAccessToken(accessCode: string, nowMs = Date.now()): string { if (!Number.isSafeInteger(nowMs) || nowMs < 0) { throw new TypeError('Access token issue time must be a non-negative safe integer'); } const timestamp = nowMs.toString(); const signature = createHmac('sha256', accessCode).update(timestamp).digest('hex'); return `${timestamp}.${signature}`; } /** Verify an HMAC-signed token against the access code */ export function verifyAccessToken( token: string, accessCode: string, options: AccessTokenValidationOptions = {}, ): boolean { const parsed = parseFreshAccessToken(token, { ...options, ttlSeconds: options.ttlSeconds ?? resolveAccessCodeSessionTtlSeconds(process.env.ACCESS_CODE_SESSION_TTL_SECONDS), }); if (!parsed) return false; const expected = createHmac('sha256', accessCode).update(parsed.issuedAtText).digest('hex'); const sigBuf = Buffer.from(parsed.signatureHex, 'hex'); const expBuf = Buffer.from(expected, 'hex'); if (sigBuf.length !== expBuf.length) return false; return timingSafeEqual(sigBuf, expBuf); }