diff --git a/electron/build/embedded.provisionprofile b/electron/build/embedded.provisionprofile new file mode 100644 index 00000000..61e8088a Binary files /dev/null and b/electron/build/embedded.provisionprofile differ 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"