aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/nodes.py3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/auth.py9
-rw-r--r--packages/meshbay-hub/tests/test_mnp_token.py14
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"