kv-wasm: onboarding (setup codes, enrollment tickets, scan-to-join) for the web vault
Mirrors kv-ffi's docs/10 §5 API in WebCore and binds it under the names the web app codes against: setupPayloadForKit, addDevicePayload, revokeEnrollmentTicket, loginWithSetup, joinStart/joinPoll/joinCancel, joinApprove, joinSend, and the free function parseSetupPayload (also callable through the worker client). Unlike kv-ffi, the Secret Key is not persisted: every unlock on the web takes it, so it is kept zeroized in the unlocked state and dropped on lock. loginWithSetup refuses a code for a different server than the configured origin (the browser only talks to its own origin). Tests: ticket enrollment and scan-to-join in both roles against kv-ffi via the kv-sync test server, a swapped-approver rejection, a Chrome test (IndexedDB never holds the Secret Key, JS shapes, error codes) and a Node smoke check of the built package. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Vendored
+2
@@ -6,6 +6,7 @@ import type {
|
||||
totpCode,
|
||||
matchUrl,
|
||||
coreVersion,
|
||||
parseSetupPayload,
|
||||
} from "./kv_wasm.js";
|
||||
|
||||
export * from "./kv_wasm.js";
|
||||
@@ -22,6 +23,7 @@ export type RemoteVault = CoreMethods & {
|
||||
totpCode: Async<typeof totpCode>;
|
||||
matchUrl: Async<typeof matchUrl>;
|
||||
coreVersion: Async<typeof coreVersion>;
|
||||
parseSetupPayload: Async<typeof parseSetupPayload>;
|
||||
/** Frees the core (zeroizing keys), releases the tab lock and terminates the worker. */
|
||||
close(): Promise<void>;
|
||||
};
|
||||
|
||||
@@ -92,6 +92,21 @@ assert.equal(kv.generatePassword(20, false).length, 20);
|
||||
const exported = await a.exportEncrypted("export passphrase");
|
||||
assert.ok(exported instanceof Uint8Array && exported.length > 100);
|
||||
|
||||
// Onboarding (docs/10): the Kit's Setup Code round-trips through the free parser.
|
||||
const k = await kv.VaultCore.newInMemory();
|
||||
const kSecret = await k.createAccount(PW);
|
||||
await k.configureServer("https://vault.example.org", []);
|
||||
const setup = kv.parseSetupPayload(await k.setupPayloadForKit());
|
||||
assert.deepEqual(setup, {
|
||||
serverUrl: "https://vault.example.org",
|
||||
spkiPins: [],
|
||||
accountId: await k.accountId(),
|
||||
secretKey: kSecret,
|
||||
ticket: null,
|
||||
});
|
||||
await rejects(k.joinPoll(), "Invalid");
|
||||
k.free();
|
||||
|
||||
if (process.argv.includes("--write-fixture")) {
|
||||
const items = [];
|
||||
for (const s of await a.listItems()) items.push(await a.getItem(s.id));
|
||||
|
||||
@@ -56,7 +56,7 @@ async function open(dbName, inMemory) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const FREE_FUNCTIONS = new Set(["generatePassword", "totpCode", "matchUrl", "coreVersion"]);
|
||||
const FREE_FUNCTIONS = new Set(["generatePassword", "totpCode", "matchUrl", "coreVersion", "parseSetupPayload"]);
|
||||
|
||||
self.onmessage = async ({ data: { id, method, args } }) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user