Files
AfiS-Aktensystem/web/lib/export/token.ts
T
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

63 lines
2.4 KiB
TypeScript

import { createHmac, timingSafeEqual } from 'node:crypto'
import { getEnv } from '@/lib/env'
/**
* Kurzlebiges Zeichen für den Druckabruf.
*
* Der PDF-Dienst ruft die Druckansicht selbst auf und braucht dafür die Rechte
* des Abrufers. Ihm dafür das **Sitzungscookie** mitzugeben wäre der bequeme
* Weg und der falsche: ein Cookie ist der volle Zugang zum Konto, und es läge
* dann im Speicher eines zweiten Prozesses und in dessen Protokollen.
*
* Stattdessen ein signiertes Zeichen, das genau eines erlaubt: diese eine
* Ansicht, für diesen einen Auftrag, für ein paar Minuten. Es taugt zu nichts
* anderem. Vergleichbar mit der signierten Datei-URL aus docs/07.
*/
/** Lange genug für einen langsamen Rendervorgang, kurz genug zum Wegwerfen. */
const GUELTIG_MS = 10 * 60_000
export type RenderClaim = {
jobId: string
userId: string
clearanceLevel: number
expiresAt: number
}
function signieren(nutzlast: string): string {
return createHmac('sha256', getEnv().AUTH_SECRET).update(nutzlast).digest('base64url')
}
export function createRenderToken(claim: Omit<RenderClaim, 'expiresAt'>): string {
const vollstaendig: RenderClaim = { ...claim, expiresAt: Date.now() + GUELTIG_MS }
const nutzlast = Buffer.from(JSON.stringify(vollstaendig)).toString('base64url')
return `${nutzlast}.${signieren(nutzlast)}`
}
/**
* Prüft das Zeichen. Gibt `null` zurück, wenn irgendetwas nicht stimmt - der
* Aufrufer macht daraus 404, nicht 403: dass es die Druckansicht gibt, geht
* niemanden etwas an, der kein gültiges Zeichen hat.
*/
export function verifyRenderToken(token: string | null | undefined): RenderClaim | null {
if (!token) return null
const [nutzlast, unterschrift] = token.split('.')
if (!nutzlast || !unterschrift) return null
const erwartet = signieren(nutzlast)
// Zeitkonstanter Vergleich: ein Längenvergleich vorweg, weil timingSafeEqual
// bei ungleicher Länge wirft.
if (erwartet.length !== unterschrift.length) return null
if (!timingSafeEqual(Buffer.from(erwartet), Buffer.from(unterschrift))) return null
try {
const claim = JSON.parse(Buffer.from(nutzlast, 'base64url').toString('utf8')) as RenderClaim
if (typeof claim.expiresAt !== 'number' || claim.expiresAt < Date.now()) return null
if (typeof claim.userId !== 'string' || typeof claim.jobId !== 'string') return null
return claim
} catch {
return null
}
}