Skip to content

feat(webauthn): Touch ID + Google passkey sign-in via patched from-source CEF#25

Open
wenkaifan0720 wants to merge 1 commit into
mainfrom
feat/webauthn-passkey-auth-window
Open

feat(webauthn): Touch ID + Google passkey sign-in via patched from-source CEF#25
wenkaifan0720 wants to merge 1 commit into
mainfrom
feat/webauthn-passkey-auth-window

Conversation

@wenkaifan0720

Copy link
Copy Markdown
Collaborator

What

Adds on-device Touch ID passkeys and Google-account QR/phone-passkey sign-in to the CEF webview, via a patched from-source CEF — Tier-2 (build libcef with a 13-line patch), not a Chromium fork.

Two independent pieces:

  1. openAuthWindow op (protocol v3 → v4) — new IPC op 0x39 that opens a windowed Chrome-runtime browser (CEF_RUNTIME_STYLE_CHROME) sharing the OSR tile's cookie jar. The OSR/windowless tile can't host WebAuthn UI; this runs the ceremony in a window that can. Plumbed main.mmFlutterCefPlugin.swift / CefWebSession.swift / CefProfileHost.swiftcef_web_controller.dart (openAuthWindow(url)).

  2. From-source patched CEFnative/build-cef-from-source.sh fetches Chromium+CEF at the pinned rev and applies one 13-line patch (native/patches/campus_webauthn_keychain.patch) that hardcodes the KLAJ5X6PJP team prefix on the WebAuthn keychain-access-group. Unbranded CEF derives it from an empty MAC_TEAM_IDENTIFIER_STRING.org.chromium.Chromium.webauthn, which no entitlement can match, so Touch ID is unreachable without this.

Why the build config matters

is_official_build=true is load-bearing three ways — don't downgrade to a bare dcheck_always_on=false build:

  • matches how the stock Spotify prebuilt is built;
  • strips DCHECKs (a plain build crashes at chrome_paths_mac.mm on CEF's Versions/A layout);
  • fixes Google-account caBLE/QR passkey sign-in — a non-official build breaks it ("Something went wrong" after the email step); the official build completes the flow.

proprietary_codecs=true ffmpeg_branding="Chrome" add H.264/AAC — royalty-bearing (MPEG-LA / Via-LA). Gate prod distribution on legal sign-off.

No-drift

A patched framework under the same CEF_VERSION would collide with the stock content hash → every consumer silently fetches the unpatched framework. CEF_FRAMEWORK_VARIANT (campus-webauthn-h264) is folded into cef_host_hash.sh's digest so the patched build gets a distinct GCS key, and build_cef_host.sh hard-fails if the marker is non-stock but the framework lacks the patch. The stock digest is byte-identical to before (no republish of the existing stock host).

Testing

Notarized Campus dev build, user-confirmed:

  • ✅ webauthn.io Touch ID registration/auth
  • ✅ Google-account QR / phone-passkey sign-in completes (reaches Google's "add a passkey" enrollment page)

Note: the -67030 process_requirement.cc log line is a benign red herring — present in the working build too. Ignore it; watch the actual login flow.

Merge / rollout notes

  • After merge, publish the patched cef_host prebuilt for its new content hash (make publish-cef-host) so consumers fetch it instead of a ~2 hr local build.
  • work_canvas then bumps its pin to this merge SHA (make pin-cef) — companion PR.
  • entitlements.browser.plist is a documented hand-apply reference; the work_canvas release script derives browser entitlements from the provisioning profile at sign time.

🤖 Generated with Claude Code

…ed from-source CEF

OSR/windowless browsers can't host the WebAuthn UI, and unbranded CEF can't
reach the macOS platform authenticator (Touch ID / Secure Enclave) at all --
its keychain-access-group derives from an empty MAC_TEAM_IDENTIFIER_STRING, so
it asks for ".org.chromium.Chromium.webauthn", which no entitlement can match.

This adds first-class passkey support in two independent pieces:

1. openAuthWindow op (protocol v3 -> v4): a new IPC op (0x39) that opens a
   windowed Chrome-runtime browser (CEF_RUNTIME_STYLE_CHROME) sharing the OSR
   tile's cookie jar, so the sign-in ceremony runs somewhere that can host the
   WebAuthn UI the off-screen tile cannot. Plumbed through main.mm ->
   FlutterCefPlugin.swift / CefWebSession.swift / CefProfileHost.swift ->
   cef_web_controller.dart (openAuthWindow(url)).

2. From-source patched CEF build: native/build-cef-from-source.sh fetches
   Chromium+CEF at the pinned rev and applies ONE 13-line patch
   (native/patches/campus_webauthn_keychain.patch) hardcoding the KLAJ5X6PJP
   team prefix on the keychain group. This is Tier-2 (build libcef from source
   with a patch), NOT a Chromium fork -- the patch is a single entry in CEF's
   own patch.cfg pipeline.

   Build config is load-bearing: is_official_build=true (matches the stock
   Spotify prebuilt, strips DCHECKs, and -- critically -- fixes Google-account
   caBLE/QR passkey sign-in that a non-official build breaks). H.264/AAC via
   proprietary_codecs + ffmpeg_branding="Chrome" (royalty-bearing; gate prod
   distribution on legal sign-off).

No-drift: a patched framework built under the same CEF_VERSION would otherwise
collide with the stock content hash and every consumer would silently fetch the
UNPATCHED framework. CEF_FRAMEWORK_VARIANT ("campus-webauthn-h264") is folded
into cef_host_hash.sh's digest so the patched build gets a DISTINCT GCS key, and
build_cef_host.sh hard-fails if the marker is non-stock but the framework lacks
the patch.

Signing: the passkey keychain group needs the browser (top-level cef_host)
process entitled + an embedded Developer-ID provisioning profile blessing that
group; helpers keep the minimal entitlements. entitlements.browser.plist
documents the browser-process superset (the work_canvas release script derives
it from the profile at sign time).

Tested: webauthn.io Touch ID and Google-account QR/phone-passkey sign-in both
complete end-to-end in a notarized Campus dev build (reaches Google's passkey
enrollment page). The -67030 process_requirement log line is a benign red
herring -- present in the working build too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant