05/18 Update documents
This commit is contained in:
@@ -272,6 +272,54 @@ Requires Chrome 111+. `content.js` `onChanged` listener watches `area === 'sessi
|
||||
|
||||
## Key Implementation Details
|
||||
|
||||
### `password_changed_at` — accurate password age tracking
|
||||
|
||||
Stored as `plain.password_changed_at: ISO-8601 string` inside the encrypted `enc_data` blob. Server never sees it.
|
||||
|
||||
**Written by:**
|
||||
- `vault.js` `handleFormSubmit` — **create**: always set to `now`. **Edit**: only updated when the password field value actually changed vs. the decrypted existing item in `_items`. Unchanged password → existing `password_changed_at` preserved. No existing timestamp + unchanged → absent (dashboard falls back to `created_at`).
|
||||
- `popup.js` `saveCredential` and `addItemToVault` — always set to `now` for new extension-saved credentials.
|
||||
- `toggleFavorite` — spreads `{ ...item.plain, tags: newTags }`, automatically preserving the timestamp.
|
||||
|
||||
**Read by:**
|
||||
- `renderSecurityDashboard` — `ageRef = plain?.password_changed_at || created_at`; items with a recent `password_changed_at` are no longer falsely flagged as old even if the item itself is old.
|
||||
- Backwards compatible: items without `password_changed_at` fall back to `created_at`.
|
||||
|
||||
### Auto-lock on tab visibility change
|
||||
|
||||
`_startWebIdleTracking()` now registers a `visibilitychange` listener in addition to the mouse/keyboard events.
|
||||
|
||||
**Hidden:** clears the inactivity timer (user cannot be active on a hidden tab), records `_hiddenAt = Date.now()`.
|
||||
|
||||
**Visible again:** compares elapsed hidden time against the idle timeout.
|
||||
- Hidden `≥ timeout` → immediate lock (same `VaultSession.clear()` + `showUnlockOverlay()` + toast as inactivity lock).
|
||||
- Hidden `< timeout` → clears `_hiddenAt`, resumes timer from zero.
|
||||
|
||||
This catches screen-lock, minimize, and long tab switches. Short tab switches (< timeout) do not trigger a lock.
|
||||
|
||||
### Emergency access stale snapshot detection
|
||||
|
||||
`EmergencyAccess._enc_vault_is_legacy()` — parses `enc_vault` JSON server-side (no decryption) and returns `True` if any item has a `name` key but lacks `enc_name`. Exposed as `enc_vault_is_legacy` in `to_dict()`.
|
||||
|
||||
`renderEmergencyGrants()` — `ready` grants now show three states:
|
||||
- `enc_vault_is_legacy: true` → amber `⚠ Outdated snapshot` badge + **Re-provision** button
|
||||
- `enc_vault_is_legacy: false` → secondary **Update Recovery Data** button
|
||||
- `accepted` (no snapshot yet) → primary **Provide Recovery Data** button
|
||||
|
||||
After re-provisioning, `enc_vault_is_legacy` returns `false` and the warning disappears.
|
||||
|
||||
### Extension health badge
|
||||
|
||||
`_runPopupHealthCheck()` in `popup.js` — fires after every `fetchAndDecryptVault()`. Computes weak/reused synchronously, sends a preliminary `HEALTH_UPDATE` to the background SW, then runs HIBP in parallel (`_popupCheckHibp`). Sends a final `HEALTH_UPDATE` with breach count.
|
||||
|
||||
`background.js` / `background.firefox.js`:
|
||||
- `HEALTH_UPDATE` handler: stores `{ breached, weak, reused }` in `chrome.storage.local` as `health_status`, calls `applyHealthBadge()`.
|
||||
- `applyHealthBadge()`: on tabs with no match-count badge, shows red `⚠` (breach) or amber `⚠` (weak/reused). Never overwrites the blue match-count badge or the pending-save `!` badge.
|
||||
- `CLEAR_SAVE_BADGE`: after clearing `!`, immediately re-applies health badge.
|
||||
- Idle lock: removes `health_status` from local storage.
|
||||
|
||||
**Badge priority:** pending-save `!` (red) > match count (blue) > health `⚠` (red/amber).
|
||||
|
||||
### Vault health notifications (background checks)
|
||||
|
||||
`runBackgroundHealthCheck()` fires after every `loadVault()` call — async, non-blocking.
|
||||
@@ -342,7 +390,7 @@ DELETE /api/webauthn/credentials/<id> → remove a passkey
|
||||
|
||||
**`auth.js` login wiring:** "Sign in with Passkey" button on `login.html` calls `PasskeyAuth.loginWithPasskey(email)`. On success, stores tokens and navigates to `/vault` — unlock overlay fires if master password field was empty.
|
||||
|
||||
**Authenticator attachment:** currently `PLATFORM` (biometrics / device passkey). To support roaming authenticators (YubiKey, phone-as-key), remove or change `authenticator_attachment` in `register_begin`.
|
||||
**Authenticator attachment:** `register_begin` accepts optional `attachment` in the POST body: `"platform"` (default — device biometrics) or `"cross-platform"` (roaming — YubiKey, phone QR, NFC). The settings UI exposes a `<select>` with both options. Credential type is shown in the passkeys list using transport hints (`internal` → 📱 Device, `usb/nfc/ble` → 🔑 Security key).
|
||||
|
||||
**Exception names** (`webauthn>=2.0`):
|
||||
- `InvalidRegistrationResponse` — use in `register_complete`
|
||||
@@ -509,6 +557,9 @@ Audit log details **never** contain plaintext item names, shared item names, or
|
||||
- `_validate_folder_id` must be called for any user-supplied `folder_id` before DB write
|
||||
- `verify_auth_token` must receive `user=user` at login to enable Argon2 rehash
|
||||
- APScheduler cleanup job handles `TokenBlacklist`, `RecoveryChallenge`, AND `TotpUsedCode`
|
||||
- `password_changed_at` lives inside `plain` (encrypted) — never in the server schema
|
||||
- WebAuthn `attachment`: `"cross-platform"` for security keys; `"platform"` for device biometrics (default)
|
||||
- `enc_vault_is_legacy` check in `EmergencyAccess.to_dict()` is pure JSON inspection — no decryption
|
||||
|
||||
---
|
||||
|
||||
@@ -616,6 +667,9 @@ webauthn>=2.0 # Passkey / WebAuthn (py-webauthn)
|
||||
- Never log decrypted vault data server-side — audit details use `item_type` + `id` only
|
||||
- Background health check runs after every vault load — badge + banner update without user action
|
||||
- HIBP results cached in `_healthCache` per session — Security tab reuses them, no double-query
|
||||
- Extension health badge stored in `chrome.storage.local` (`health_status`) — survives SW restarts
|
||||
- `password_changed_at` in `plain` enables accurate old-password detection without schema changes
|
||||
- Tab visibility lock fires immediately when hidden duration ≥ idle timeout — catches screen lock
|
||||
- Tags live in `enc_data` as `plain.tags: string[]` — no schema change ever needed
|
||||
- `vault_items_cs` is in `chrome.storage.session` — decrypted data never written to disk
|
||||
- HIBP checks run progressively — synchronous sections render first, then parallel async checks
|
||||
|
||||
Reference in New Issue
Block a user