Main-thread authenticatedXrpc calls share session with worker

did:plc:wydyrngmxbcsqdvhmd7whmye opened this Mar 20, 2026 0 comments
did:plc:wydyrngmxbcsqdvhmd7whmye opened Mar 20, 2026

Summary

Several components make authenticated PDS calls via authenticatedXrpc on the main thread, loading session directly from IndexedDB. If a main-thread XRPC call overlaps with a worker PDS call, both read/write the same OAuthSession in IndexedDB — same DPoP nonce field, same last-writer-wins race as the worker-internal issue.

Affected call sites

High risk (can race with cabinet operations)

Low risk (user-initiated, not concurrent with cabinet)

Root cause

authenticatedXrpc in lib/api.ts loads session from IndexedDB, creates a DPoP proof, makes the request, and handles token refresh — all on the main thread. The worker's session gate does the same thing independently. Both compete for the same IndexedDB session record.

Proposed fix

Migrate high-risk call sites to worker PDS methods (most already have WASM equivalents):

  • revokeGrantworker.grantRevoke() (exists)
  • listOutgoingGrantsworker.listGrantsRaw() + client-side filtering
  • ShareDialog grant creation → worker.documentFetchContentKey() + worker.grantCreate() (both exist)
  • Preview blob fetch → worker.documentFetchContentKey() through gate + unauthenticated getBlob (blobs are public in atproto), or add a WASM export that returns full metadata
  • createPendingShare → needs new WASM export (no equivalent yet)

Low-risk call sites can remain on main thread for now.

Note on blob fetch

com.atproto.sync.getBlob may or may not require authentication depending on the PDS implementation. The Rust opake-core attaches auth headers to getBlob calls. The sharing.ts incoming grant flow fetches blobs without auth and it works for cross-PDS access. Testing needed to confirm whether the user's own PDS requires auth for getBlob.

No activity yet.

cospan · schematic version control on atproto built on AT Protocol