diff options
Diffstat (limited to 'packages/meshbay-hub')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/api/nodes.py | 3 | ||||
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/auth.py | 9 | ||||
| -rw-r--r-- | packages/meshbay-hub/tests/test_mnp_token.py | 14 |
3 files changed, 24 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/nodes.py b/packages/meshbay-hub/src/meshbay_hub/api/nodes.py index b158621..b35f11e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/nodes.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/nodes.py @@ -64,7 +64,8 @@ async def mnp_token( # `not_a_member`, which is the answer that case has always had. "mnp_token": issue_mnp_token(current_user.id, groups=[group_id] if member else [], - node_pk=(body.node_pk if body else "")), + node_pk=(body.node_pk if body else ""), + username=current_user.username), "expires_in": 900, } diff --git a/packages/meshbay-hub/src/meshbay_hub/auth.py b/packages/meshbay-hub/src/meshbay_hub/auth.py index 1d09581..41fb159 100644 --- a/packages/meshbay-hub/src/meshbay_hub/auth.py +++ b/packages/meshbay-hub/src/meshbay_hub/auth.py @@ -241,7 +241,8 @@ def issue_access_token( def issue_mnp_token(user_id: str, groups: list[str] | None = None, - node_pk: str | None = None, ttl: int = 900) -> str: + node_pk: str | None = None, ttl: int = 900, + username: str = "") -> str: """Issue the short-lived token a member presents to a node in the handshake. `aud=MNP_AUD`, so it is accepted by `authorize_token` and refused by the hub @@ -254,6 +255,11 @@ def issue_mnp_token(user_id: str, groups: list[str] | None = None, cannot be replayed to another node the member also belongs to — the node checks it in `authorize_token` (E10). The client knows the target node's key before it connects and asks for a token bound to it. + + `username` is the account's name, so the node can show a person by name in + its audit log and roster. Admission by link, by device or into an open group + carries no name of its own; without this the node knew those members only + by id. It is a label, never authority: the node decides on `sub`. """ if _hub_sk_pem is None: raise RuntimeError("Hub keypair not loaded") @@ -261,6 +267,7 @@ def issue_mnp_token(user_id: str, groups: list[str] | None = None, payload = { "iss": _hub_id, "sub": user_id, + "username": username, "jti": str(uuid.uuid4()), "iat": now, "exp": now + ttl, diff --git a/packages/meshbay-hub/tests/test_mnp_token.py b/packages/meshbay-hub/tests/test_mnp_token.py index ef6423d..71d7ff6 100644 --- a/packages/meshbay-hub/tests/test_mnp_token.py +++ b/packages/meshbay-hub/tests/test_mnp_token.py @@ -136,3 +136,17 @@ async def test_a_token_must_name_its_group(client): r = await client.post("/v1/nodes/mnp-token", json={}, headers={"Authorization": f"Bearer {tok}"}) assert r.status_code == 422 + + +@pytest.mark.asyncio +async def test_mnp_token_names_the_account(client): + """A member admitted by link has no name in the node's roster but this one; + without it the node's audit log shows a bare id.""" + from meshbay_hub.auth import hub_public_key_pem + + tok = await _session_token(client, "mnp_named") + gid = await _own_group(client, tok) + mnp = (await client.post("/v1/nodes/mnp-token", json={"group_id": gid}, + headers={"Authorization": f"Bearer {tok}"})).json()["mnp_token"] + peer = authorize_token(mnp, hub_public_key_pem(), group_id=gid) + assert peer.username == "mnp_named" |