You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The keystore record carried:
secretKey: sk, // Will be moved to largeBlob
Nothing ever moved it. That was the only occurrence of the field in all of
src/, and storeEncryptedKeystore() serialises a fixed whitelist which does not
include it — so the key was dropped at persist time. Choosing largeBlob
produced a keystore that worked for exactly the session that created it and
could never be unlocked afterwards: the next load called
retrieveSKFromLargeBlob(), read a blob nobody had written, and failed with "No
largeBlob data found in credential".
Worth stating plainly, because the first suspicion was worse: the key never
reached disk in the clear. The whitelist that dropped it also kept it out of
localStorage. The feature was inert, not leaky.
writeSKToLargeBlob() now performs the assertion that stores it — a blob can
only be written during an assertion, never at registration, which is why
addLargeBlobToCredentialOptions() alone persists nothing and why this costs one
extra prompt. It throws unless the authenticator reports `written: true`.
Failing there beats persisting a record that cannot be opened, and it is the
same lesson as the rest of this branch: read what happened, do not trust what
was requested.
Tests: 5 new cases covering confirmed write, declined write, absent result, a
truthy-but-not-true value, and a refused assertion. 63 node tests pass.
One wrinkle worth recording: Node defines globalThis.navigator as a getter-only
accessor, so the obvious `globalThis.navigator = stub` throws
TypeError: Cannot set property navigator. The stub swaps the property
descriptor and restores the original afterwards.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0 commit comments