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"