diff options
Diffstat (limited to 'packages/meshbay-hub/src')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/api/users.py | 9 | ||||
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/auth-page.js | 9 | ||||
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/keyderive.js | 6 |
3 files changed, 20 insertions, 4 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py index 559cfa6..9b70b59 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/users.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py @@ -150,8 +150,13 @@ async def register( return {"user_id": found.id, "email_verification_required": True} raise HTTPException(status_code=409, detail="Username already taken") - # Captcha gate — web path only (native clients send auth_key) - if _cfg and _cfg.captcha.enabled and not body.auth_key: + # Captcha gate — every fresh registration when a captcha is configured, with + # no client carve-out. The earlier `and not body.auth_key` exempted anything + # that sent an `auth_key`, which is *every* real client (the browser sends it + # too, from the password split) — so the check was off for everyone, and a + # bot skipped it by sending the field. The desktop client is Chromium and + # renders the same widget, so it has no need of an exemption either. + if _cfg and _cfg.captcha.enabled: await _verify_captcha_or_raise(body.captcha_token, request) # Email uniqueness (only active or pending accounts) diff --git a/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js b/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js index df08bc4..4c00137 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js @@ -237,8 +237,11 @@ export function RegisterPage() { const rk = window.MeshBayKeys.generateRecoveryKey(); // `name` (trimmed), not the raw field: the hub stores the trimmed // username and every key derivation must fold in the same string. + // `captcha.token` rides along — the submit button is already disabled + // until it is set when a captcha is configured (see the form below). await window.MeshBayKeys.registerUser( - name, email, password, emailRecovery ? rk.mnemonic : null); + name, email, password, emailRecovery ? rk.mnemonic : null, + captcha.token); setRecoveryMnemonic(rk.mnemonic); session.recoveryKey = await window.MeshBayKeys.deriveRecoveryKey(rk.mnemonic, name); @@ -257,6 +260,10 @@ export function RegisterPage() { } } catch (err) { setError(err.message); + // A reCAPTCHA token is single-use: after a failed attempt (name taken, + // e-mail in use…) it is spent, so clear it and make the user solve a + // fresh one before the next try. No-op when no captcha is configured. + captcha.reset(); } finally { setLoading(false); } diff --git a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js index 0aaa6a5..a540a94 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js @@ -276,7 +276,7 @@ async function decryptBundle(bundleB64, password, username) { * * Returns the raw private keys for immediate use after registration. */ -async function registerUser(username, email, password, recoveryMnemonic) { +async function registerUser(username, email, password, recoveryMnemonic, captchaToken) { // No keypair here any more. Identity keys are per node: one is generated the // first time this account joins a given node, encrypted under the passphrase, // and left with that node. So an operator who cracks what sits on their own @@ -291,6 +291,10 @@ async function registerUser(username, email, password, recoveryMnemonic) { // appends it to the verification e-mail and stores it nowhere // (docs/auth-confirm.md §4.4). Omitted when they chose to save it themselves. if (recoveryMnemonic) payload.recovery_key = recoveryMnemonic; + // reCAPTCHA response, when the hub has a captcha configured. The widget lives + // in RegisterPage (auth-page.js); this function just forwards its token. A + // hub with no captcha configured sends nothing and the server does not check. + if (captchaToken) payload.captcha_token = captchaToken; const resp = await hubCall('/v1/users/register', { method: 'POST', |