ae31fd7fa22f

wire PASSKEY_AUTH_RESPONSE in messages.ts and toast-based get approval in passkey.ts

time
2026-05-13 15:05 UTC · 5 months ago
machine
koh2
parent
b8e642f2990b
3 files src/background/messages.ts+12 −0src/background/passkey.ts+39 −1src/shared/types.ts+3 −0
src/background/messages.ts+12 −0
@@ -1062,6 +1062,18 @@10621062         return await handleFirefoxPasskeyGet(payload);10631063       }10641064 1065+      case "PASSKEY_AUTH_RESPONSE": {1066+        const { requestId, approved } = (msg.payload ?? {}) as {1067+          requestId?: string;1068+          approved?: boolean;1069+        };1070+        if (!requestId || typeof approved !== 'boolean') {1071+          return { ok: false, error: 'requestId and approved required' };1072+        }1073+        await resolvePendingPasskeyRequest(Number(requestId), approved);1074+        return { ok: true };1075+      }1076+10651077       case "GET_PASSKEY_FOR_DOMAIN": {10661078         const { domain } = (msg.payload ?? {}) as { domain?: string };10671079         if (!domain) return { ok: false, error: "domain required" };

view file

src/background/passkey.ts+39 −1
@@ -406,6 +406,44 @@406406   });407407 }408408 409+async function promptViaToast(request: PendingPasskeyRequest): Promise<boolean> {410+  if (chrome.storage.session) {411+    await chrome.storage.session.set({ [pendingStorageKey(request.requestId)]: request });412+  }413+414+  const tabs = await new Promise<chrome.tabs.Tab[]>((resolve) => {415+    chrome.tabs.query({ active: true, currentWindow: true }, resolve);416+  });417+  const tab = tabs[0];418+419+  if (tab?.id) {420+    chrome.tabs.sendMessage(tab.id, {421+      type: 'SHOW_PASSKEY_TOAST',422+      payload: {423+        site: request.rpId,424+        displayName: request.userDisplayName ?? request.userName,425+        username: request.userName,426+        requestId: String(request.requestId),427+      },428+    }).catch(() => {429+      // Content script not loaded on this tab — fall back to popup.430+      void openApprovalPopup();431+    });432+  } else {433+    await openApprovalPopup();434+  }435+436+  return new Promise<boolean>((resolve) => {437+    const timeoutId = setTimeout(async () => {438+      approvals.delete(request.requestId);439+      await clearPendingRequest(request.requestId);440+      resolve(false);441+    }, APPROVAL_TIMEOUT_MS);442+443+    approvals.set(request.requestId, { resolve, timeoutId });444+  });445+}446+409447 async function handleGetRequest(410448   request: chrome.webAuthenticationProxy.GetRequest411449 ): Promise<void> {@@ -454,7 +492,7 @@454492     return;455493   }456494 457-  const approved = await promptUserApproval({495+  const approved = await promptViaToast({458496     requestId: request.requestId,459497     type: "get",460498     rpId: credential.rpId,

view file

src/shared/types.ts+3 −0
@@ -324,6 +324,9 @@324324   | "PRO_GATE_TRIGGERED" // background → popup: a Pro feature was attempted on free325325   | "PASSKEY_PRO_GATE" // background → content: show passkey Pro gate in inline popup326326   | "OPEN_PRO_GATE" // content → background: open popup and navigate to License section327+  | "SHOW_PASSKEY_TOAST"    // background → content tab: show passkey auth toast328+  | "DISMISS_PASSKEY_TOAST" // background → content tab: dismiss passkey auth toast329+  | "PASSKEY_AUTH_RESPONSE" // content → background: user approved or denied the toast327330   | "SHOULD_PLAY_CELEBRATION"328331   | "MARK_CELEBRATION_PLAYED";329332 

view file