feat: Anmelden mit Passkey
Passkeys (WebAuthn/FIDO2) als zweiter Anmeldeweg - auf der Anmeldeseite und bei der erneuten Bestaetigung vor sensiblen Aktionen. Verwaltet unter "Mein Konto". Der Passkey ERSETZT den zweiten Faktor, statt zu ihm hinzuzukommen. Das ist tragbar, weil er zwei Faktoren in sich vereint: Besitz des Geraets und dessen Entsperrung. Damit das keine Annahme bleibt, wird die Nutzerverifikation angefordert (userVerification: 'required') und serverseitig nachgeprueft - eine Antwort ohne sie wird abgewiesen. TOTP muss vorher eingerichtet sein. Ein Passkey haengt an einem Geraet; waere er der einzige Weg hinein, koennte ein verlorenes Handy die Akte schliessen. Aufbau: - Die Pruefung laeuft vollstaendig im Credentials-Provider "passkey", nicht in der Server Action davor. Ein "war schon geprueft" ueber die Grenze zu reichen waere die Stelle, an der sich Anmeldungen faelschen lassen. - Challenges liegen serverseitig und werden per DELETE genau einmal verbraucht - die Datenbank entscheidet, wer sie bekommt. - Bereichskennung und Herkunft kommen aus AUTH_URL, nicht aus einer zweiten Konfigurationsquelle. - Ohne https wird der Knopf gar nicht erst angeboten. - Rate-Limiting, Kontosperre und Audit-Log gelten unveraendert; die Anmeldung nennt den verwendeten Passkey namentlich. Zwei Anmerkungen zur Migration: - "prisma migrate diff" wollte zusaetzlich die Trigramm-Indizes aus prisma/sql/suche.sql loeschen, die Prisma nicht kennt. Die Zeilen sind entfernt und im Migrationskopf vermerkt - ein DROP haette die unscharfe Suche stillgelegt, ohne dass etwas fehlgeschlagen waere. - "prisma format" hat das Schema durchgaengig neu ausgerichtet. Ohne Whitespace betrachtet sind es 88 hinzugefuegte Zeilen und keine geloeschte. Geprueft: 21 Einheitstests, drei End-to-End-Tests gegen Chromiums virtuellen Authenticator (einrichten, anmelden, bestaetigen, entfernen, Geraet ohne Verifikation abgewiesen), beide Geraeteprofile. 496 Tests, null Typfehler, Produktionsbau steht. Nebenbei: Die Zahlen im README waren veraltet (24 statt 33 Modelle, 46 statt 79 Seiten). Sie stehen jetzt mit dem Befehl daneben, mit dem man sie nachzaehlt.
This commit is contained in:
+76
-1
@@ -26,6 +26,80 @@ Wiederherstellung*.
|
||||
|
||||
_Noch nichts._
|
||||
|
||||
## [1.1.0] – 2026-08-22
|
||||
|
||||
### Hinzugefügt
|
||||
|
||||
- **Anmelden mit Passkey.** Auf der Anmeldeseite steht jetzt unter dem
|
||||
Formular ein zweiter Weg: *Mit Passkey anmelden*. Ein Fingerabdruck, ein
|
||||
Blick in die Kamera oder die Geräte-PIN — keine E-Mail, kein Passwort,
|
||||
kein abgetippter Code.
|
||||
|
||||
**Der Passkey ersetzt den zweiten Faktor, er kommt nicht zusätzlich.**
|
||||
Das ist kein Nachlassen: Ein Passkey verlangt den Besitz des Geräts
|
||||
*und* dessen Entsperrung, das sind dieselben zwei Faktoren wie vorher.
|
||||
Die Anwendung fordert die Nutzerverifikation ausdrücklich an und weist
|
||||
eine Antwort ohne sie zurück. Ein Gerät, das auf blosses Antippen hin
|
||||
signiert, kommt nicht herein.
|
||||
|
||||
In einem Punkt ist er dem bisherigen Weg sogar überlegen: Ein Passkey
|
||||
ist an die Adresse gebunden, unter der er eingerichtet wurde. Auf einer
|
||||
nachgebauten Anmeldeseite funktioniert er nicht — und Phishing ist der
|
||||
übliche Weg, an einen TOTP-Code zu kommen.
|
||||
|
||||
**Die Authenticator-App muss trotzdem eingerichtet sein**, bevor sich
|
||||
ein Passkey anlegen lässt. Ein Passkey hängt an einem Gerät. Ginge das
|
||||
verloren und wäre er der einzige Weg hinein, käme niemand mehr an die
|
||||
Akte — und darin stehen Unterlagen, die eine betreute Person braucht.
|
||||
Der Passkey ist die bequeme Tür, nicht die einzige.
|
||||
|
||||
Verwaltet werden sie unter **Mein Konto → Passkeys**: einrichten,
|
||||
benennen, entfernen. Die Liste zeigt, wann ein Gerät zuletzt benutzt
|
||||
wurde und ob der Schlüssel gesichert ist (iCloud, Google, Bitwarden)
|
||||
oder nur auf diesem einen Gerät liegt. Einrichten und Entfernen stehen
|
||||
im Protokoll, jede Anmeldung nennt den verwendeten Passkey namentlich.
|
||||
|
||||
- **Bestätigen mit Passkey.** Export, endgültiges Löschen und die
|
||||
Benutzerverwaltung verlangen eine frische Anmeldung. Auch die geht jetzt
|
||||
mit dem Passkey. Geprüft wird zusätzlich, dass er zum angemeldeten
|
||||
Zugang gehört — sonst liesse sich mit dem eigenen Finger eine fremde,
|
||||
offen stehende Sitzung freischalten. Genau der Fall, gegen den diese
|
||||
Zwischenfrage gebaut ist.
|
||||
|
||||
### Zu beachten
|
||||
|
||||
- **Passkeys brauchen `https`.** Der Browser gibt das Verfahren nur in
|
||||
einem sicheren Kontext frei. Läuft die Anwendung ohne TLS, erscheint der
|
||||
Knopf gar nicht erst, statt beim Klick mit einer kryptischen Meldung
|
||||
abzubrechen.
|
||||
|
||||
- **`AUTH_URL` muss stimmen — jetzt noch mehr als vorher.** Aus ihr wird
|
||||
die Kennung abgeleitet, an die der Authenticator den Schlüssel bindet.
|
||||
Steht dort eine andere Adresse als die im Browser, lässt sich zwar ein
|
||||
Passkey einrichten, aber nie wieder verwenden.
|
||||
|
||||
**Ein Wechsel der Domain macht alle Passkeys unbrauchbar.** Das ist
|
||||
keine Panne, sondern der Phishingschutz. Nach einem Umzug richtet man
|
||||
sie neu ein; die Anmeldung mit Passwort und Code bleibt währenddessen
|
||||
offen.
|
||||
|
||||
- **Eine Datenbank-Migration ist nötig** (`20260822160000_passkey_anmeldung`).
|
||||
Sie läuft beim Start des Behälters automatisch mit. Bestehende Zugänge
|
||||
sind nicht betroffen: Ohne eingerichteten Passkey ändert sich nichts.
|
||||
|
||||
### Geprüft
|
||||
|
||||
- 21 neue Prüfungen (`tests/passkey.test.ts`), vor allem für die Herleitung
|
||||
von Kennung und Herkunft aus `AUTH_URL`: Port, Schrägstrich am Ende,
|
||||
Unterdomain, `http` gegen `https`, `localhost`. Das ist die Stelle, an
|
||||
der eine Installation still kaputtginge — dieselbe Fehlerklasse wie
|
||||
seinerzeit bei `APP_BIND` und `ENC_KEY_V2`.
|
||||
- Drei End-to-End-Tests gegen einen echten Browser (`e2e/passkey.spec.ts`),
|
||||
mit Chromiums virtuellem Authenticator: einrichten, abmelden, mit Passkey
|
||||
anmelden, Benutzung vermerkt, entfernen. Dazu die Bestätigung vor dem
|
||||
Vollexport und der Fall, dass ein Gerät ohne Nutzerverifikation nicht
|
||||
hereinkommt.
|
||||
|
||||
## [1.0.5] – 2026-08-22
|
||||
|
||||
### Hinzugefügt
|
||||
@@ -392,7 +466,8 @@ Was davon wann angegangen wird, steht in [`docs/ROADMAP.md`](docs/ROADMAP.md).
|
||||
|
||||
---
|
||||
|
||||
[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.5...HEAD
|
||||
[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.1.0...HEAD
|
||||
[1.1.0]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.5...v1.1.0
|
||||
[1.0.5]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.4...v1.0.5
|
||||
[1.0.4]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.3...v1.0.4
|
||||
[1.0.3]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.2...v1.0.3
|
||||
|
||||
Reference in New Issue
Block a user