diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/hub-client.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/hub-client.js | 26 |
1 files changed, 20 insertions, 6 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js b/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js index 16dce9f..e72961c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js @@ -89,7 +89,12 @@ async function clearAllCachedIndexes() { // a code is single-use and short-lived. A plain object, not two bare `let`s, // so importing modules can update either field without this module handing // out a rebindable export. -const session = { bundleKey: null, pendingJoinCode: null }; +// +// `recoveryKey` (docs/auth-confirm.md §4.3) is the AES key that wraps the +// *recovery* copy of an identity bundle. In-memory only, and set only when the +// user has just generated or entered the recovery secret (registration, or the +// Flow B screen) — it cannot be re-derived from the passphrase. +const session = { bundleKey: null, pendingJoinCode: null, recoveryKey: null }; function _openKeyDB() { return new Promise((resolve, reject) => { @@ -99,25 +104,33 @@ function _openKeyDB() { req.onerror = () => reject(req.error); }); } -async function _storeBundleKey(key) { +async function _storeKey(slot, key) { try { const db = await _openKeyDB(); const tx = db.transaction('k', 'readwrite'); - tx.objectStore('k').put(key, 'bk'); + tx.objectStore('k').put(key, slot); await new Promise(r => { tx.oncomplete = r; }); db.close(); } catch {} } -async function _loadBundleKey() { +async function _loadKey(slot) { try { const db = await _openKeyDB(); const tx = db.transaction('k', 'readonly'); - const g = tx.objectStore('k').get('bk'); + const g = tx.objectStore('k').get(slot); const val = await new Promise(r => { g.onsuccess = () => r(g.result); }); db.close(); return val || null; } catch { return null; } } +// 'bk' = passphrase-derived bundle key; 'rk' = recovery key (docs/auth-confirm.md +// §4.3). Persisting 'rk' is what lets a group joined in a *later* session still +// get a recovery-wrapped identity copy, instead of only groups joined in the +// unbroken session that generated it. Cleared with everything else on sign-out. +const _storeBundleKey = (key) => _storeKey('bk', key); +const _loadBundleKey = () => _loadKey('bk'); +const _storeRecoveryKey = (key) => _storeKey('rk', key); +const _loadRecoveryKey = () => _loadKey('rk'); async function _clearKeyDB() { try { const db = await _openKeyDB(); @@ -142,6 +155,7 @@ function saveAuth(auth) { } else { localStorage.removeItem(AUTH_KEY); session.bundleKey = null; + session.recoveryKey = null; _clearKeyDB(); } } @@ -274,7 +288,7 @@ async function hubFetch(path, { method = 'GET', body, token, _retried } = {}) { export { HUB, navigate, session, cacheGroupIndex, getCachedGroupIndex, getAllCachedIndexes, clearAllCachedIndexes, - _storeBundleKey, _loadBundleKey, _clearKeyDB, + _storeBundleKey, _loadBundleKey, _storeRecoveryKey, _loadRecoveryKey, _clearKeyDB, loadAuth, saveAuth, setAuth, setAuthChangeListener, tokenLifeLeft, refreshAccessToken, ensureFreshToken, hubFetch, }; |