Verwaltung: nur noch EINMAL den Code eingeben

Rueckmeldung 22.08.2026: "ich hab auf der verwaltungs seite 2 mal dass ich
den zugangscode eingeben muss und vanvan auch, ich will es nur einmal
eingeben muessen." Berechtigt.

Die Zugangswand hat serverseitig laengst geprueft, WER da ist -- Dogfather
oder VanVan, laut Masterplan S.4 beide gleichberechtigte
Volladministratoren. Ein zweites Passwort danach bringt keinen
zusaetzlichen Schutz, es kostet nur jedes Mal Zeit.

Warum es nicht einfach "Cookie mitschicken" ist: Die Verwaltungsdaten
liegen hinter postfach.dogfather-universe.com, einem ANDEREN Rechnernamen.
Das Sitzungs-Cookie gilt dort nicht -- so sind Cookies gebaut, und das ist
gut so.

Loesung: Die Zugangswand stellt unter /webdesign/api-ausweis einen
kurzlebigen, signierten Ausweis aus, den die interne API anerkennt.
  - 15 Minuten gueltig, die Seite holt bei Bedarf still einen neuen
  - traegt bereich="wd-admin", damit ein Sitzungs-Token der Zugangswand
    hier NICHT durchgeht und umgekehrt
  - nur mit gueltiger Zugangssitzung zu bekommen
  - gilt AUSSCHLIESSLICH fuer die /webdesign-Endpunkte. Postfach,
    Bewerbungen, Supporter und Teamverwaltung bleiben unberuehrt

Fehlt WEBDESIGN_API_SECRET auf einer der beiden Seiten, gilt kein Ausweis
und die Verwaltung fragt wie bisher nach dem Team-Code. Ein fehlender
Konfigurationswert darf niemals eine Tuer oeffnen -- nur eine schliessen.
Genau das prueft der letzte Testfall.

Bei einem 401 wird EINMAL still ein neuer Ausweis geholt und die Anfrage
wiederholt, statt jemanden mitten im Arbeiten rauszuwerfen.

10/10 Tests, darunter: gefaelschte Signatur, fremdes Geheimnis,
abgelaufen, falscher Bereich, erfundene Rolle.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-08-22 21:36:11 +02:00
co-authored by Claude Opus 5
parent 2542a2598e
commit 74091590b8
8 changed files with 298 additions and 9 deletions
+53
View File
@@ -318,6 +318,59 @@ export function webdesignGate(req, res, next) {
return wdAbmeldung(req, res);
}
/* ---------------------------------------------------------------------
AUSWEIS FÜR DIE INTERNE API
Rückmeldung 22.08.2026: "ich hab auf der verwaltungs seite 2 mal dass
ich den zugangscode eingeben muss und vanvan auch, ich will es nur
einmal eingeben müssen."
Berechtigt. Die Zugangswand hat serverseitig bereits geprüft, WER da
ist — Dogfather oder VanVan, beide laut Masterplan S.4
gleichberechtigte Volladministratoren. Ein zweites Mal danach zu
fragen bringt keinen zusätzlichen Schutz, es nervt nur.
Warum es trotzdem nicht einfach "das Cookie mitschicken" ist:
Die Verwaltungsdaten liegen hinter postfach.dogfather-universe.com,
einem ANDEREN Rechnernamen. Das Sitzungs-Cookie dieser Seite gilt
dort nicht — so sind Cookies gebaut, und das ist auch gut so.
Deshalb stellt diese Stelle einen kurzlebigen, signierten Ausweis
aus, den die interne API prüfen kann:
- nur 15 Minuten gültig (die Seite holt sich bei Bedarf einen neuen)
- trägt bereich="wd-admin", damit er NICHT als Sitzung dieser Seite
durchgeht und umgekehrt
- nur mit gültiger Zugangssitzung zu bekommen
Fehlt WEBDESIGN_API_SECRET, wird kein Ausweis ausgestellt und die
Verwaltung fragt wie bisher nach dem Team-Code. Kein Ausfall, nur
kein Komfort — ein fehlender Wert darf nie eine Tür öffnen.
--------------------------------------------------------------------- */
if (pfad === "/webdesign/api-ausweis" && req.method === "GET") {
const secret = geheimnis();
const apiSecret = process.env.WEBDESIGN_API_SECRET;
const sitzung = secret ? wdPruefe(req.cookies?.[WD_COOKIE], secret) : null;
if (!sitzung) {
return res.status(401).json({ ok: false, error: "Keine gültige Zugangssitzung." });
}
if (!apiSecret) {
/* Bewusst 200 mit ausweis:null statt eines Fehlers: die Verwaltung
soll daraufhin ruhig ihre eigene Anmeldung zeigen, nicht eine
Störungsmeldung. */
return res.json({ ok: true, ausweis: null, grund: "nicht eingerichtet" });
}
const inhalt = JSON.stringify({
bereich: "wd-admin",
rolle: sitzung.rolle,
exp: Date.now() + 15 * 60 * 1000,
});
const nutzlast = b64url(Buffer.from(inhalt, "utf8"));
const sig = createHmac("sha256", apiSecret).update(nutzlast).digest();
return res.json({ ok: true, ausweis: `${nutzlast}.${b64url(sig)}`, rolle: sitzung.rolle });
}
// Wer bin ich? (für die persönliche Begrüßung)
if (pfad === "/webdesign/zugang-status" && req.method === "GET") {
const secret = geheimnis();