aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/app.js11
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/group-page.js9
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/keyderive.js24
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/profile-page.js4
4 files changed, 32 insertions, 16 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js
index c101ec9..72dd123 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js
@@ -1139,8 +1139,8 @@ function App() {
* exactly what a browser does, and is a worse experience rather than a
* broken one.
*/
- const registerThisDevice = useCallback(async (token) => {
- if (!platform.device.available) return;
+ const registerThisDevice = useCallback(async (token, authKey) => {
+ if (!platform.device.available || !authKey) return;
try {
const backend = await platform.secrets.backend();
if (backend === 'unavailable') return;
@@ -1148,7 +1148,7 @@ function App() {
if (!pk) return;
await hubFetch('/v1/users/devices', {
method: 'POST', token,
- body: { pk_auth_ed25519: pk, label: t('device.this_device') },
+ body: { pk_auth_ed25519: pk, label: t('device.this_device'), auth_key: authKey },
});
} catch (err) {
console.warn('device not registered:', err.message);
@@ -1158,11 +1158,12 @@ function App() {
const authCtx = {
user,
login: async (username, password) => {
- let token, refreshToken;
+ let token, refreshToken, authKey;
if (window.MeshBayKeys) {
const data = await window.MeshBayKeys.loginAndRecover(username, password);
token = data.accessToken;
refreshToken = data.refreshToken;
+ authKey = data.authKey;
// The only thing sign-in produces: the key that opens a node's bundle.
// Which identity we use is decided per node, when we get there.
session.bundleKey = data.bundleKey;
@@ -1180,7 +1181,7 @@ function App() {
// On a desktop build, remember this device so the next launch does not ask
// for the passphrase again. The key is generated and held by the main
// process; what travels here is only its public half.
- await registerThisDevice(token);
+ await registerThisDevice(token, authKey);
// Before the session lands, so the idle watch starting with it does not
// read the last-active time of whoever used this browser before.
markActive(true);
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js
index 4510848..fe858b2 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js
@@ -257,10 +257,11 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs,
setError('');
try {
// Same derivation as sign-in — the token is already ours, only the key
- // that opens node bundles is missing here, and the pepper that goes into
- // it is asked for with that token. Persisted so this browser is set up
- // from now on.
- const { pepper, version } = await window.MeshBayKeys.fetchBundlePepper(token);
+ // that opens node bundles is missing here. The hub hands the pepper that
+ // goes into it only against the passphrase proof, never the token alone.
+ // Persisted so this browser is set up from now on.
+ const authKey = await window.MeshBayKeys.deriveAuthKey(pass, username);
+ const { pepper, version } = await window.MeshBayKeys.fetchBundlePepper(token, authKey);
session.bundleKey = await window.MeshBayKeys.sessionBundleKey(
pass, username, userId, pepper, version);
await _storeBundleKey(session.bundleKey);
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js
index 9f28b5c..6bd5896 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js
@@ -195,14 +195,25 @@ async function nodeBundleKey(sessionKey, nodePkB64) {
}
/**
- * GET the pepper for a session that is already open — a stored session from
- * before the pepper existed, a passphrase change. Sign-in carries it already.
+ * The pepper for a session that is already open but lacks the key derived from
+ * it — a restored session, a passphrase change. Sign-in carries it already. The
+ * hub asks for the passphrase proof: a token alone never obtains the pepper.
*/
-async function fetchBundlePepper(token) {
+async function fetchBundlePepper(token, authKey) {
const resp = await hubCall('/v1/users/me/bundle-pepper', {
- headers: { Authorization: `Bearer ${token}` },
+ method: 'POST',
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
+ body: JSON.stringify({ auth_key: authKey }),
});
- if (!resp.ok) throw new Error(`bundle pepper: ${resp.status}`);
+ if (!resp.ok) {
+ // The hub's own words: a wrong passphrase and a locked account are what a
+ // person can act on, a bare status is not.
+ let detail = `bundle pepper: ${resp.status}`;
+ try { const j = await resp.json(); detail = j.detail || j.error || detail; } catch { /* not JSON */ }
+ const err = new Error(String(detail));
+ err.status = resp.status;
+ throw err;
+ }
const data = await resp.json();
return { pepper: data.bundle_pepper, version: data.bundle_pepper_version };
}
@@ -446,6 +457,9 @@ async function loginAndRecover(username, password) {
const result = {
accessToken: data.access_token,
refreshToken: data.refresh_token,
+ // Kept for the one call that follows a sign-in and must prove the
+ // passphrase again: registering this device's key. Not stored.
+ authKey,
// The pepper rides on the sign-in response, so this costs no extra call;
// it is folded into the key here and not kept.
bundleKey: await sessionBundleKey(
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 7c36951..1800d72 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js
@@ -147,7 +147,8 @@ export function ProfilePage({ user, onLogout }) {
// 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 oldAuthKey = await K.deriveAuthKey(cpOld, user.username);
+ const { pepper, version } = await K.fetchBundlePepper(user.token, oldAuthKey);
const oldKey = await K.sessionBundleKey(
cpOld, user.username, user.userId, pepper, version);
const newKey = await K.sessionBundleKey(
@@ -160,7 +161,6 @@ export function ProfilePage({ user, onLogout }) {
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,