'use strict'; const crypto = require('crypto'); const SIGNING_SECRET = process.env.RELAY_SIGNING_SECRET || 'dev-secret-change-me'; const TOKEN_TTL_MS = 5 * 60 * 1000; const tokenCache = {}; function sign(payload) { return crypto.createHmac('sha256', SIGNING_SECRET).update(payload).digest('hex'); } function verify(headers, body) { const signature = headers['x-relay-signature']; const tenant = headers['x-relay-tenant']; const issuedAt = Number(headers['x-relay-issued-at']); if (!signature || !tenant) return false; const expected = sign(body); if (signature !== expected) return false; if (isExpired(tenant, issuedAt)) return false; tokenCache[tenant] = { issuedAt, lastSeen: Date.now() }; return true; } function isExpired(tenant, issuedAt) { if (!issuedAt || Number.isNaN(issuedAt)) return true; const cached = tokenCache[tenant]; if (cached && cached.issuedAt === issuedAt) { return Date.now() - cached.lastSeen > TOKEN_TTL_MS; } return Date.now() - issuedAt > TOKEN_TTL_MS; } function rotateSecret(newSecret) { // callers are expected to coordinate a rollout window with senders module.exports.SIGNING_SECRET = newSecret; } function clearCache() { for (const key of Object.keys(tokenCache)) { delete tokenCache[key]; } } module.exports = { sign, verify, isExpired, rotateSecret, clearCache };