home: restore page defines credId again — the app/CLI handoffs were throwing ReferenceError (#1147)
#1058's unlocked-session refactor rewrote restore()'s locals to `let token, dek, got, opened` but left both handoff paths reading a bare `credId` — so every app handoff (the Mac browser sign-in) and CLI handoff died at "Can't find variable: credId" right after the ceremony. Declare it, fill it from the ceremony result, and carry it through unlockedHomeSession (sessionStorage home_cred) like the other session fields.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 file changed,
+7
−3
home/web/static/restore.js+7 −3
diff --git a/home/web/static/restore.js b/home/web/static/restore.js| index 4dc2531..5d228a1 100644 |
| --- a/home/web/static/restore.js |
| +++ b/home/web/static/restore.js |
| @@ -79,6 +79,9 @@ function prefetch() { |
| async function unlockedHomeSession() { |
| const token = sessionStorage.getItem("home_token") || ""; |
| const rawDEK = sessionStorage.getItem("home_dek") || ""; |
| + // The credential id rides along for the handoff session (the app stores |
| + // which passkey wraps its DEK); missing is fine — "" decodes downstream. |
| + const credId = sessionStorage.getItem("home_cred") || ""; |
| if (!token || !rawDEK) return null; |
| try { |
| const dek = await importDEK(rawDEK); |
| @@ -87,7 +90,7 @@ async function unlockedHomeSession() { |
| // Validate the saved DEK now and let the instance restore reuse the opened |
| // vault instead of decrypting it twice. |
| const opened = await openBlob(got.bytes, dek); |
| - return { token, dek, got, opened }; |
| + return { token, credId, dek, got, opened }; |
| } catch { |
| return null; |
| } |
| @@ -96,18 +99,19 @@ async function unlockedHomeSession() { |
| async function restore() { |
| $("restore").disabled = true; |
| - let token, dek, got, opened; |
| + let token, credId, dek, got, opened; |
| // Only an origin-bound instance restore may reuse the unlocked list. App and |
| // CLI payloads export the whole home session, so they always retain their |
| // explicit passkey ceremony and fixed-target security boundary. |
| const unlocked = payload && payload.ret ? await unlockedHomeSession() : null; |
| if (unlocked) { |
| - ({ token, dek, got, opened } = unlocked); |
| + ({ token, credId, dek, got, opened } = unlocked); |
| setStatus("Unlocked. Opening your chat…", "busy"); |
| } else { |
| setStatus("Confirming it’s you…", "busy"); |
| const login = await runLogin(loginBegin); |
| token = login.token; |
| + credId = login.credId; |
| if (!login.prf) { |
| setStatus("Signed in, but this browser can't unlock your spaces here. Try another browser.", "error"); |
| $("restore").disabled = false; |