Skip to content

Pending sync session crafted with empty crypto fields — silent push failures #63

@Loule95450

Description

@Loule95450

Context

The sync wizard tries OPAQUE login, falls back to OPAQUE register on a 4xx, and falls back to a synthetic pending session on the special "user exists but pending approval" 403. The code path for that 403 builds a pending session with empty deviceId/saltSync/devicePubkey/devicePrivkey/ekFingerprint (placeholders, see runner.ts:135-148).

Problem / Observation

  • extension/src/background/sync/runner.ts:135-148 saves a PendingSyncSession with empty strings for saltSync, devicePubkey, devicePrivkey, ekFingerprint.
  • The polling loop (runner.ts:195) hits /auth/approval-status/<userId>. When the admin approves, the upgrade path runs (runner.ts:208-215) and writes status: "approved" — but the device keypair fields are still empty strings.
  • Any later push (engine.ts:122-136) calls deriveEncryptionKey(session, master) which calls splitMasterKey(mk, new Uint8Array(16)) and compares the resulting EK's fingerprint to the session's ekFingerprint. The latter is the empty string — so every push throws "master mismatch" (shared/sync/auth.ts:197), which the engine swallows. The user sees a connected, approved session that never actually pushes anything.
  • Symptom from the user's perspective: "sync connected, but nothing reaches the server". Force send returns { pushed: 0, failed: N }.
  • Compounds with rotating-master scenarios: after a master change, the same path would lock the user out of sync.

Proposed approach

  1. Drop the synthetic pending-session shortcut. When the login leg returns 403 pending_approval, the runner should register (or skip if the server's identity for this email is incompatible). The synthetic session is a footgun.
  2. If the shortcut stays, complete the synthesis: derive saltSync, generate a real device keypair, derive ekFingerprint, save those. The current placeholder design saves bytes for almost no UX win.
  3. On every approved push that throws "master mismatch", surface a "Reconnect to sync" banner in the popup — currently the error is silently swallowed in syncAccountChange.

Acceptance criteria

  • runner.test.ts adds a fixture exercising the 403 pending_approval path and asserts the saved session has non-empty saltSync, devicePubkey, devicePrivkey, ekFingerprint (or no session at all).
  • A master mismatch error from deriveEncryptionKey is surfaced as a popup-level notification, not silently swallowed.
  • The popup has a path to clear and re-create a borked session without re-typing the master from scratch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium prioritybugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions