Open
Conversation
Describes the full architecture for handling navigator.credentials.get() with mediation='conditional'. Covers data flow across all three layers (tab preload -> main process -> browser chrome), key decisions (trigger on focus, close on scroll, return false on unsupported platforms), edge cases, and the file-by-file implementation plan.
Introduce PasskeyCredentialInfo, PasskeyOverlayPosition, and FlowPasskeyOverlayAPI interfaces. Register passkeyOverlay on the global flow type so both the preload and renderer can consume it.
Rewrite webauthn:get handler to detect mediation='conditional', call listPasskeys(rpId), store a long-lived promise in a ConditionalSession map, and send the passkey list to the tab preload. Add four new IPC handlers: - webauthn:conditional-input-focus: computes window-relative overlay position from tab bounds + input rect, forwards to browser chrome - webauthn:conditional-input-blur: hides the overlay - webauthn:conditional-select: calls getCredential() with the selected credential's allowCredentials, resolves the pending promise - webauthn:conditional-dismiss: resolves with NotAllowedError Add webauthn:is-conditional-available handler and cleanup on tab destruction/navigation.
Tab-side changes: - Extend PatchedCredentialsContainer with reportInputFocus, reportInputBlur, and onConditionalPasskeys IPC methods - Change isConditionalMediationAvailable from returning false to calling webauthn:is-conditional-available IPC - Replace mediation='conditional' NotSupportedError throw with full conditional flow: listen for passkey list, set up setupConditionalUI() with MutationObserver + focus/blur listeners on autocomplete='webauthn' inputs, close overlay on scroll, wire AbortSignal cleanup - Extract throwWebauthnError helper to deduplicate DOMException logic Browser-chrome-side changes: - Add passkeyOverlayAPI implementing FlowPasskeyOverlayAPI (onShow, onHide, select, dismiss) using listenOnIPCChannel - Expose as flow.passkeyOverlay via wrapAPI with 'browser' permission
Create passkey-overlay.tsx with three components following the find-in-page PortalComponent pattern: - PasskeyItem: individual passkey row with icon, userName, rpId - PasskeyDropdown: animated dropdown with keyboard navigation (arrow keys, Enter to select, Escape to dismiss), auto-focus - PasskeyOverlay: orchestrator listening to flow.passkeyOverlay onShow/onHide, rendering via PortalComponent at ViewLayer.OVERLAY Mount <PasskeyOverlay /> alongside <FindInPage /> in browser-ui main.
Contributor
Build artifacts for all platforms are ready! 🚀Download the artifacts for: One-line installer (Unstable):bunx flow-debug-build --open 22650188362(execution 22650188362 / attempt 1) |
Greptile SummaryThis PR implements conditional passkey (WebAuthn autofill) support for Flow Browser, covering the full stack from main-process IPC session management to tab-side DOM observers and a new browser-chrome dropdown overlay component. The architecture closely mirrors the existing Key areas that need attention before merging:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Website as Website (Tab)
participant Preload as Tab Preload
participant Main as Main Process IPC
participant Chrome as Browser Chrome
participant Overlay as PasskeyOverlay
Website->>Preload: credentials.get({ mediation: "conditional" })
Preload->>Main: ipcRenderer.invoke("webauthn:get", options)
Main->>Main: listPasskeys(rpId)
Main->>Preload: send("webauthn:conditional-passkeys", passkeys)
Note over Main: Long-lived Promise stored in conditionalSessions
Preload->>Preload: setupConditionalUI()<br/>MutationObserver + focus/blur listeners
Website->>Preload: User focuses input[autocomplete=webauthn]
Preload->>Main: send("webauthn:conditional-input-focus", rect)
Main->>Main: Compute overlay position<br/>(tabBounds + inputRect)
Main->>Chrome: sendMessageToCoreWebContents("webauthn:conditional-show-overlay")
Chrome->>Overlay: onShow({ passkeys, position })
Overlay->>Overlay: Render PasskeyDropdown
Overlay->>Chrome: User selects passkey
Chrome->>Main: send("webauthn:conditional-select", credentialId)
Main->>Main: getCredential(options + allowCredentials)
Main->>Main: session.resolve(result)
Main->>Preload: ipcRenderer.invoke resolves
Preload->>Website: Return PublicKeyCredential
Note over Main,Chrome: On navigation/destroy: cancelConditionalSession → hide overlay
Last reviewed commit: 7f8642f |
… keyboard nav, and tab-switch handling - Add enablePasskeyAutofill setting to disable passkey autofill globally - Hide overlay instantly on tab switch using active-tab-changed listener - Support both dark and light mode theming in overlay - Implement keyboard navigation (arrows, enter, escape) without stealing focus - Increase border/shadow visibility for the autofill portal UI
- Guard against stale session reference after async getCredential call - Use did-start-navigation instead of did-navigate for earlier cleanup - Validate credentialId against session passkey list (defense-in-depth) - Add clarifying comment on is-conditional-available handler
…e-input-event Keyboard events (arrows, enter, escape) were not reaching the passkey overlay because it runs in a separate unfocused WebContentsView. Move selection state and keyboard handling to the main process using before-input-event on the tab's webContents, which allows synchronous preventDefault (critical for Enter). Also fix scrollbar color to follow space theme instead of system theme.
Reset renderer visible state when focusedTabId no longer matches shownForTabId, preventing the overlay from reappearing without main process knowledge (which broke keyboard handling and dismiss). Gate preload dismiss handlers (scroll, focusin, pointerdown, visibilitychange) behind an overlayShown boolean to avoid unnecessary IPC traffic on every click/focus change/scroll event.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements conditional passkey (autofill) support so that when a website calls
navigator.credentials.get({ mediation: "conditional" }), Flow Browser queries available passkeys viaelectron-webauthn'slistPasskeys(rpId)API, monitors focus events on<input autocomplete="webauthn">fields, and shows a dropdown overlay near the focused input. On passkey selection, authentication completes using the existinggetCredential()flow.Changes
design/CONDITIONAL_PASSKEY_SUPPORT.md) — full architecture, data flow, edge casessrc/shared/flow/interfaces/browser/passkey-overlay.ts) —PasskeyCredentialInfo,PasskeyOverlayPosition,FlowPasskeyOverlayAPIinterfaces; registered on the globalflowtypesrc/main/ipc/webauthn/index.ts) — conditional session tracking with long-lived promises,listPasskeys()integration, overlay position computation from tab bounds, select/dismiss handlers, cleanup on tab destruction/navigationsrc/preload/index.ts) —reportInputFocus,reportInputBlur,onConditionalPasskeysIPC methods;setupConditionalUI()with MutationObserver + focus/blur/scroll listeners forautocomplete="webauthn"inputs;isConditionalMediationAvailable()now returns true on macOSsrc/preload/index.ts) —passkeyOverlayAPIexposed asflow.passkeyOverlaywithonShow,onHide,select,dismisssrc/renderer/src/components/browser-ui/passkey-overlay.tsx) —PasskeyOverlaycomponent usingPortalComponentpattern (matching find-in-page), with animated dropdown, keyboard navigation, and auto-focusKey Design Decisions
falsefromisConditionalMediationAvailable()Requirements
listPasskeys()requires this at the OS levelelectron-webauthn@^1.2.0— provideslistPasskeys(relyingPartyId: string)Validation
bun lint— passesbun typecheck— passes (both node and web configs)bun format— all files clean