Countdown des 2FA-Codes: Die Anzeige zaehlte blind im Sekundentakt herunter und lief dadurch aus dem Takt. Am Ende eines Zeitfensters wurde zwar ein neuer Code angefordert, der Zaehler aber nicht angehalten - bei langsamer oder fehlschlagender Antwort lief er ins Negative und stiess jede Sekunde eine weitere Anfrage an (inkl. Audit-Eintrag je Abruf), waehrend die Anzeige auf dem alten Wert stehenblieb. Die Restlaufzeit wird jetzt aus einem festen Ablaufzeitpunkt berechnet, sodass gedrosselte oder ausgefallene Ticks sie nicht verschieben. Pro Ablauf wird genau einmal nachgeladen; liefert der Server keinen Code mehr (gesperrt/offline), stoppt der Timer und die Anzeige wird geleert. Dieselbe Rechnung gilt fuer die Benachrichtigung im Seiteninhalt; die Periode steckt dort wie im Popup in einer Konstanten statt als 30 im Ausdruck. Manueller API-Token: Eingabefeld, "Token speichern" und "Verbindung testen" sind aus den Optionen entfernt; die Server-URL laesst sich weiterhin separat speichern. getAccessToken() kennt nur noch den SSO-Weg, und ein aus einer frueheren Version uebernommener Token wird bei onInstalled aus chrome.storage.local geloescht. Doku und Store-Listing entsprechend angepasst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G12DRMpe4UjYDwuRU1sjy1
5.3 KiB
Architektur
Überblick
Die Erweiterung ist ein Manifest-V3-Client ohne eigenen Server. Sie besteht aus vier Kontexten, die
über chrome.runtime-Nachrichten kommunizieren:
┌───────────────┐ Messages ┌──────────────────────┐ HTTPS/Bearer ┌──────────────────┐
│ popup.html │ ───────────▶ │ background.js │ ───────────────▶ │ OpenNIT-Server │
│ popup.js │ ◀─────────── │ (Service Worker) │ ◀─────────────── │ /api/vault/... │
└───────────────┘ │ - Cache (5 Min) │ └──────────────────┘
┌───────────────┐ Messages │ - Lock-Gate │
│ content.js │ ───────────▶ │ - Favicon-Cache │
│ (jede Seite) │ ◀─────────── │ - Clipboard-Timer │
└───────────────┘ └──────────┬───────────┘
│ CLIP_WRITE
┌────────▼─────────┐
│ offscreen.html │ (Zwischenablage leeren)
└──────────────────┘
Komponenten
| Datei | Rolle |
|---|---|
background.js |
Zentrale Logik: API-Aufrufe, 5-Minuten-Cache, Lock-Gate, Favicon-Cache (Data-URLs), Zwischenablage-Timer. Alle Secrets fließen hier durch. |
content.js |
Wird auf jeder Seite ausgeführt. Erkennt Benutzer-/Passwort-/OTP-Felder (inkl. Shadow-DOM, mehrstufige Logins, segmentierte OTP-Felder), zeigt das Vorschlags-Dropdown und füllt Felder framework-kompatibel (React/Vue/Angular). |
popup.html / popup.js |
Toolbar-Popup: Liste, Suche, Detailansicht, Anlegen + Generator, PIN-Schirm. |
options.html / options.js |
Einstellungen: Server-URL, SSO-Anmeldung, PIN-Sperrdauer, Zwischenablage. |
offscreen.html / offscreen.js |
Minimaldokument, das ausschließlich die Zwischenablage leert (MV3-konform). |
Nachrichten (Auszug)
| Typ | Von → Nach | Zweck |
|---|---|---|
CHECK_STATUS |
popup/content → bg | Token prüfen, App-Name/User, pin_enabled |
GET_LOCK / DO_UNLOCK / LOCK_NOW |
popup → bg | PIN-Sperre abfragen/entsperren/sperren |
GET_ENTRIES / GET_MATCHING_ENTRIES |
popup/content → bg | Einträge (alle / passend zur URL) |
GET_PASSWORD / GET_TOTP |
popup/content → bg | Secret on demand |
CREATE_ENTRY |
popup → bg | Neuen Eintrag anlegen |
GET_FAVICON |
popup/content → bg | Favicon als Data-URL (serverseitig gecacht) |
VAULT_FILL |
popup → content | Aktives Tab-Formular ausfüllen |
SCHEDULE_CLIP_CLEAR |
popup/content → bg | Zwischenablage-Leerung planen |
Server-API (in OpenNIT)
Alle Endpunkte unter /api/vault/extension/ mit Authorization: Bearer <token>:
GET /entries– Liste (Titel, Benutzer, URL, Notizen,has_totp,favicon_domain,has_favicon)GET /entries/{id}/password– Passwort (protokolliert im Audit-Log)GET /entries/{id}/totp– aktueller TOTP-Code + RestsekundenGET /entries/{id}/favicon?fetch=1– gecachtes Favicon (bei Bedarf serverseitig geholt)POST /entries– neuen Eintrag anlegenGET /status– Token gültig? +pin_enabled/pin_lock_secsPOST /unlock– Tresor-PIN verifizieren + serverseitiges Entsperr-Fenster für den Token setzenPOST /lock– Token sofort wieder sperren (Entsperr-Fenster zurücksetzen)GET /oauth/authorize·POST /oauth/authorize– SSO-Anmeldung/Zustimmung (Session, PKCE)POST /oauth/token– Authorization-Code- bzw. Refresh-Grant (öffentlich, PKCE) → Access/RefreshPOST /oauth/revoke– Refresh-Kette widerrufen (Logout)
Details zum SSO-Flow: SSO-PLAN.md. Die Erweiterung nutzt SSO über chrome.identity;
Access-Tokens werden im Hintergrund still per Refresh (mit Rotation) erneuert.
Lock-Gate (serverseitig erzwungen)
Ist für den Nutzer ein Tresor-PIN aktiv, liefern die Server-Endpunkte für Einträge/Passwort/TOTP
erst nach frischer PIN-Entsperrung Daten (unlocked_until pro Token) und antworten sonst mit HTTP 423.
Der Client spiegelt den Zustand nur (PIN-Schirm) – die eigentliche Durchsetzung liegt im Server, damit ein
gestohlener Token allein wertlos ist. Der Client hält seinen Entsperr-Status zusätzlich in
chrome.storage.session (verfällt beim Schließen des Browsers). Die Einstellung „PIN-Sperre" legt nur die
Fensterdauer fest; „Bis der Browser geschlossen wird" nutzt ein langes Serverfenster + Client-Sitzungsende.
Sicherheitsprinzipien
- Kein Remote-Code – alle Skripte im Paket (MV3-CSP-konform, keine Inline-Skripte).
- Secrets on demand – Passwörter/TOTP erst bei Nutzung, nie in der Liste.
- Kein persistentes Secret – nur URL, Sitzungstoken, Einstellungen in
chrome.storage. - Server-seitige Krypto – Ver-/Entschlüsselung im OpenNIT-Server, nicht im Browser.