From 6d167392f6f8ede37e2794a68a3738f8ba03131d Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Wed, 30 Sep 2026 16:58:31 +0200 Subject: feat(client): the desktop application keeps M and every node identity in its main process keyring.js derives, opens, mints, seals, signs and agrees there; the page gets public keys and a handle. Argon2 comes from the page's own WebAssembly build (Electron's crypto has none). Without OS key storage the page keeps its keys as a browser does. A node's bundle is settled after connecting, re-sealed when the key changed. Co-Authored-By: Claude Opus 5.5 --- .../src/meshbay_hub/static/profile-page.js | 51 ++++++++++++++-------- 1 file changed, 32 insertions(+), 19 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/profile-page.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js b/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js index 7a309a9..cd00f03 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js @@ -4,6 +4,7 @@ import { import { t } from './i18n.js'; import { ask } from './ask.js'; import { Icon } from './icon.js'; +import * as platform from './platform.js'; import { hubFetch, HUB, session, setAuth, _storeBundleKey, _loadBundleKey, _storeRecoveryKey, @@ -143,33 +144,45 @@ export function ProfilePage({ user, onLogout }) { // run at all the account is left untouched. // Both keys from the passphrases, with the pepper this open session asks // for: the old one opens the bundles as they are, the new one seals them. + // In the desktop application both are kept by its main process, the new + // one set aside until the hub has accepted the change. const K = window.MeshBayKeys; const { pepper, version } = await K.fetchBundlePepper(user.token); - const oldKey = await K.deriveBundleSessionKey( + const oldKey = await K.sessionBundleKey( cpOld, user.username, user.userId, pepper, version); - const newKey = await K.deriveBundleSessionKey( - cpNew, user.username, user.userId, pepper, version); - const result = await window.MeshBayTransport.rewrapAllNodes({ - hubUrl: HUB, token: user.token, - username: user.username, userId: user.userId, - bundleKey: oldKey, newBundleKey: newKey, - onProgress: setCpProgress, - }); - - const oldAuthKey = await window.MeshBayKeys.deriveAuthKey(cpOld, user.username); - const newAuthKey = await window.MeshBayKeys.deriveAuthKey(cpNew, user.username); - const resp = await hubFetch('/v1/users/password', { - method: 'POST', token: user.token, - body: { old_auth_key: oldAuthKey, new_auth_key: newAuthKey }, - }); + const newKey = await K.sessionBundleKey( + cpNew, user.username, user.userId, pepper, version, { pending: true }); + let resp; + try { + const result = await window.MeshBayTransport.rewrapAllNodes({ + hubUrl: HUB, token: user.token, + username: user.username, userId: user.userId, + bundleKey: oldKey, newBundleKey: newKey, + onProgress: setCpProgress, + }); + const oldAuthKey = await window.MeshBayKeys.deriveAuthKey(cpOld, user.username); + const newAuthKey = await window.MeshBayKeys.deriveAuthKey(cpNew, user.username); + resp = await hubFetch('/v1/users/password', { + method: 'POST', token: user.token, + body: { old_auth_key: oldAuthKey, new_auth_key: newAuthKey }, + }); + setCpResult(result); + } catch (err) { + if (newKey.native) await platform.keys.dropPending(user.userId); + throw err; + } // Keep this tab signed in with the fresh pair, and move the session's // bundle key forward so the next node connection opens the new bundles. setAuth({ ...user, token: resp.access_token, refreshToken: resp.refresh_token }); - session.bundleKey = newKey; - _storeBundleKey(newKey); + if (newKey.native) { + await platform.keys.commitPending(user.userId); + session.bundleKey = { ...newKey, pending: false }; + } else { + session.bundleKey = newKey; + _storeBundleKey(newKey); + } - setCpResult(result); setCpPhase('done'); } catch (err) { const msg = err.message === 'account_locked' -- cgit v1.2.3