Files
Webklar-Kundenbereich/server/services/signedLinks.js
T

28 lines
1.4 KiB
JavaScript

import { createHmac, timingSafeEqual } from 'node:crypto'
import { config } from '../config.js'
/**
* Signierte Datei-Links fuer das Ticketsystem (Spec 2026-10-01, Abschnitt 4 "Dateien ohne Header"): <img>, <audio>
* und Downloads koennen keinen Authorization-Header tragen. Der Link gilt 15 Minuten und nur fuer genau diese
* Datei dieses Kunden; signiert mit dem Sitzungs-Geheimnis des Portals.
*/
export const FILE_LINK_TTL_MS = 15 * 60 * 1000
const digest = (kind, customerId, id, exp) =>
createHmac('sha256', config.sessionSecret).update(`${kind}:${customerId}:${id}:${exp}`).digest('hex')
export function signFileLink({ kind, customerId, id, now = Date.now() }) {
const exp = now + FILE_LINK_TTL_MS
return { exp, sig: digest(kind, customerId, id, exp) }
}
/** exp kommt aus der Adresse (Text oder Zahl); falsch bei Ablauf, fremder Datei oder kaputter Signatur. */
export function verifyFileLink({ kind, customerId, id, exp, sig, now = Date.now() }) {
const expiry = typeof exp === 'number' ? exp : /^\d+$/.test(String(exp ?? '')) ? Number(exp) : NaN
if (!Number.isFinite(expiry) || now >= expiry) return false
if (typeof sig !== 'string' || !/^[0-9a-f]{64}$/.test(sig)) return false
const expected = Buffer.from(digest(kind, customerId, id, expiry), 'hex')
const given = Buffer.from(sig, 'hex')
return given.length === expected.length && timingSafeEqual(given, expected)
}