From cfaddf11267a42248f44300460f0d028b274189f Mon Sep 17 00:00:00 2001 From: ciregenz Date: Sun, 28 Jun 2026 15:10:59 -0700 Subject: [PATCH] [eric] passkey: enable macOS Touch ID WebAuthn (electron 42.0.0->42.3.3 for the localizedReason crash fix + Developer ID provisioning profile + keychain entitlement; reject-shim now Windows-only) --- electron/build/embedded.provisionprofile | Bin 0 -> 12268 bytes electron/build/entitlements.mac.inherit.plist | 20 +++++++++++++++ electron/build/entitlements.mac.plist | 5 ++++ electron/main.js | 24 +++++++++++++++++- electron/package-lock.json | 10 +++----- electron/package.json | 8 ++++-- electron/scripts/build-mouseclamp.sh | 3 ++- 7 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 electron/build/embedded.provisionprofile create mode 100644 electron/build/entitlements.mac.inherit.plist diff --git a/electron/build/embedded.provisionprofile b/electron/build/embedded.provisionprofile new file mode 100644 index 0000000000000000000000000000000000000000..61e8088a0fb1f2dbdb34c33ff882d2eaae6363fb GIT binary patch literal 12268 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 KGGVlQ%YOmJ>N`yU literal 0 HcmV?d00001 diff --git a/electron/build/entitlements.mac.inherit.plist b/electron/build/entitlements.mac.inherit.plist new file mode 100644 index 00000000..7491bffe --- /dev/null +++ b/electron/build/entitlements.mac.inherit.plist @@ -0,0 +1,20 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-jit + + com.apple.security.cs.disable-library-validation + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + com.apple.security.inherit + + + diff --git a/electron/build/entitlements.mac.plist b/electron/build/entitlements.mac.plist index 7491bffe..57f69cd7 100644 --- a/electron/build/entitlements.mac.plist +++ b/electron/build/entitlements.mac.plist @@ -16,5 +16,10 @@ com.apple.security.inherit + + keychain-access-groups + + Y26NUZH4NG.com.clusterlabs.openswarm.webauthn + diff --git a/electron/main.js b/electron/main.js index b4bc3086..26a6e4d0 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1713,6 +1713,16 @@ app.whenReady().then(async () => { // Off-window mouse-release crash dodge (macOS). Safe to call before windows exist. installMacMouseClamp(); + // PASSKEY SPIKE (macOS only): turn on the Secure-Enclave/Touch ID WebAuthn authenticator that Electron 42 added. Without this, isUserVerifyingPlatformAuthenticatorAvailable() is hardwired false (why the old reject-shim existed). keychainAccessGroup MUST match the keychain-access-groups entitlement (Y26NUZH4NG..webauthn) or this throws. Windows has no equivalent, so the reject-shim still runs there. + if (process.platform === 'darwin' && typeof app.configureWebAuthn === 'function') { + try { + app.configureWebAuthn({ touchID: { keychainAccessGroup: 'Y26NUZH4NG.com.clusterlabs.openswarm.webauthn', promptReason: 'sign in to $1' } }); + console.log('[passkey] configureWebAuthn(touchID) enabled'); + } catch (e) { + console.warn('[passkey] configureWebAuthn failed (entitlement missing? unsigned dev build?):', e && e.message); + } + } + // Cold-launch: if the OS opened us via openswarm:// (Windows/Linux it's // in argv; macOS fires open-url AFTER whenReady which we handle above) // route through forwardDeepLinkToRenderer so the URL gets stashed under @@ -1780,6 +1790,17 @@ app.whenReady().then(async () => { configureBrowsingSession(session.defaultSession); configureBrowsingSession(session.fromPartition(BROWSER_PARTITION)); + // PASSKEY SPIKE: when a site offers several discoverable passkeys, Electron fires this so we pick one; without a handler the WebAuthn flow stalls. For the spike just take the first; a real impl would surface a picker. macOS-only event (no-op elsewhere). + for (const ses of [session.defaultSession, session.fromPartition(BROWSER_PARTITION)]) { + try { + ses.on('select-webauthn-account', (event, accounts, callback) => { + console.log('[passkey] select-webauthn-account, n=', accounts && accounts.length); + event.preventDefault(); + callback((accounts && accounts[0] && accounts[0].accountId) || null); + }); + } catch (_) {} + } + // Add a "Google Chrome" brand to the browser partition's sec-ch-ua request hints so they match the navigator.userAgentData patch injected on dom-ready and the spoofed Chrome UA string; a Chrome UA paired with Chromium-only hints is the embedded-app tell aggressive anti-bot (Cloudflare) flags on a real human. Scoped to the browser partition, the app's own file:// + localhost traffic is untouched. const addGoogleChromeBrand = (value) => { if (typeof value !== 'string' || value.includes('"Google Chrome"')) return value; @@ -2345,7 +2366,8 @@ app.on('web-contents-created', (_event, contents) => { // navigator.credentials so passkey calls reject cleanly and post a // tagged message back; webview-preload.js listens and forwards to the // embedder, which surfaces the "Passkeys aren't supported" dialog. - contents.on('dom-ready', () => { + // PASSKEY SPIKE: Windows ONLY now. On macOS the real Secure-Enclave authenticator (app.configureWebAuthn above) handles passkeys, so rejecting would defeat the whole point; Windows still has no platform authenticator in Electron, so the reject-shim stays there. + if (process.platform === 'win32') contents.on('dom-ready', () => { contents.executeJavaScript(` (function() { if (window.__openswarm_passkey_shim__) return; diff --git a/electron/package-lock.json b/electron/package-lock.json index c40a46c0..493bfc3b 100644 --- a/electron/package-lock.json +++ b/electron/package-lock.json @@ -15,7 +15,7 @@ "devDependencies": { "@electron/notarize": "3.1.1", "cross-env": "7.0.3", - "electron": "github:castlabs/electron-releases#v42.0.0+wvcus", + "electron": "github:castlabs/electron-releases#v42.3.3+wvcus", "electron-builder": "^26.8.1", "electron-builder-squirrel-windows": "^26.8.1" } @@ -567,7 +567,6 @@ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1435,7 +1434,6 @@ "integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", @@ -1535,8 +1533,8 @@ } }, "node_modules/electron": { - "version": "42.0.0+wvcus", - "resolved": "git+ssh://git@github.com/castlabs/electron-releases.git#8cf1c79cc0a6aa09227513368166e5f67d2b680f", + "version": "42.3.3+wvcus", + "resolved": "git+ssh://git@github.com/castlabs/electron-releases.git#d2397960c342a332694ab6b059520439c8cdf49e", "dev": true, "license": "MIT", "dependencies": { @@ -1584,7 +1582,6 @@ "integrity": "sha512-o288fIdgPLHA76eDrFADHPoo7VyGkDCYbLV1GzndaMSAVBoZrGvM9m2IehdcVMzdAZJ2eV9bgyissQXHv5tGzA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", @@ -2888,7 +2885,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/electron/package.json b/electron/package.json index bfa2d323..d40da8f1 100644 --- a/electron/package.json +++ b/electron/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@electron/notarize": "3.1.1", "cross-env": "7.0.3", - "electron": "github:castlabs/electron-releases#v42.0.0+wvcus", + "electron": "github:castlabs/electron-releases#v42.3.3+wvcus", "electron-builder": "^26.8.1", "electron-builder-squirrel-windows": "^26.8.1" }, @@ -58,8 +58,12 @@ "category": "public.app-category.developer-tools", "hardenedRuntime": true, "notarize": false, + "extendInfo": { + "NSFaceIDUsageDescription": "OpenSwarm uses Touch ID to sign you in to websites with passkeys." + }, + "provisioningProfile": "build/embedded.provisionprofile", "entitlements": "build/entitlements.mac.plist", - "entitlementsInherit": "build/entitlements.mac.plist", + "entitlementsInherit": "build/entitlements.mac.inherit.plist", "extraResources": [ { "from": "build-staging/mouseclamp/${arch}", diff --git a/electron/scripts/build-mouseclamp.sh b/electron/scripts/build-mouseclamp.sh index f05b0379..403a4be3 100755 --- a/electron/scripts/build-mouseclamp.sh +++ b/electron/scripts/build-mouseclamp.sh @@ -5,9 +5,10 @@ set -euo pipefail ARCH="${1:?usage: build-mouseclamp.sh }" -ELECTRON_TARGET="42.0.0" HERE="$(cd "$(dirname "$0")/.." && pwd)" # electron/ +# Derive the node-gyp header target from the actually-installed electron so a version bump (e.g. 42.0.0 -> 42.3.3) is auto-tracked instead of silently building against stale headers. Strip any +wvcus suffix; node-gyp wants a plain semver. +ELECTRON_TARGET="$(node -p "require('$HERE/node_modules/electron/package.json').version.split('+')[0]" 2>/dev/null || echo '42.3.3')" SRC="$HERE/native/mouseclamp" OUT="$HERE/build-staging/mouseclamp/$ARCH" NODE_GYP="$HERE/node_modules/.bin/node-gyp"