diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-09 14:50:22 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-09 14:50:22 +0200 |
| commit | aed220d9f0bab42efd57b56851319e840ab8ae26 (patch) | |
| tree | e8b72fbe9016635438e6b7046a35e47ec3dbe93a /packages/meshbay-hub/src/meshbay_hub/api/users.py | |
| parent | 608d3a705d065d6b378f4889322ff9d1bc41d147 (diff) | |
| download | meshbay-aed220d9f0bab42efd57b56851319e840ab8ae26.tar.gz | |
feat: password-based key derivation + operational QUICKSTART
keyderive.py: derive Ed25519+X25519 from username+password via Argon2id.
Same credentials → same keys on any device. Encrypt/decrypt keypair
bundle (AES-256-GCM) for hub storage (web clients).
7/7 tests. Full suite: 81/81.
keyderive.js: browser counterpart using PBKDF2-SHA512 + random keypairs
encrypted for hub storage. Avoids algorithm mismatch with Python.
hub/models.py + users.py: keypair_bundle field added to User, stored on
registration, returned in login response for web client key recovery.
QUICKSTART.md: fully rewritten. 3 operational scripts in QE/demo-v1/:
setup_demo.py — create accounts, group, distribute GEK
run_node.py — start HTTP node (watches shared/ directory)
download.py — bob login → GEK fetch → decrypt → save
All tested locally end-to-end. No invented URLs.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/users.py')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/api/users.py | 11 |
1 files changed, 8 insertions, 3 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py index 0b615a4..5a7a3b4 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/users.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py @@ -42,8 +42,9 @@ class RegisterRequest(BaseModel): username: str email: str password: str - pk_user_ed25519: str # base64 raw 32B - pk_user_x25519: str # base64 raw 32B + pk_user_ed25519: str # base64 raw 32B + pk_user_x25519: str # base64 raw 32B + keypair_bundle: str | None = None # AES-GCM encrypted bundle (web clients) @field_validator("username") @classmethod @@ -95,6 +96,7 @@ async def register( pk_ed25519=body.pk_user_ed25519, pk_x25519=body.pk_user_x25519, hub_id=hub_id, + keypair_bundle=body.keypair_bundle, ) db.add(user) db.add(IPLog( @@ -142,12 +144,15 @@ async def login( db.add(IPLog(user_id=user.id, event="login", ip_address=ip)) await db.commit() - return { + resp = { "access_token": access_token, "refresh_token": raw_rt, "token_type": "bearer", "expires_in": _ttl(), } + if user.keypair_bundle: + resp["keypair_bundle"] = user.keypair_bundle # encrypted, for web clients + return resp @router.post("/token/refresh") |