aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/nodes.py24
1 files changed, 19 insertions, 5 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/nodes.py b/packages/meshbay-hub/src/meshbay_hub/api/nodes.py
index 6205180..403f450 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/nodes.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/nodes.py
@@ -21,10 +21,18 @@ from meshbay_hub.db.models import GroupMember, IPLog, Node, User
router = APIRouter(prefix="/v1/nodes", tags=["nodes"])
+class MnpTokenRequest(BaseModel):
+ # The base64 Ed25519 key of the node this token is for. The token is bound
+ # to it (E10), so it cannot be replayed to another node. The client knows it
+ # from `/v1/groups/{id}/nodes` before it connects.
+ node_pk: str = ""
+
+
@router.post("/mnp-token")
@limiter.limit("60/minute")
async def mnp_token(
request: Request,
+ body: MnpTokenRequest | None = None,
current_user: User = Depends(require_user_scope),
db: AsyncSession = Depends(get_db),
):
@@ -32,16 +40,22 @@ async def mnp_token(
Asked for with the member's own session token (require_user_scope, so a node
daemon token cannot mint one). The result carries the member's current group
- membership and `aud=MNP_AUD`, so it authorises the member to a node and is
- refused by the hub API. Short-lived on purpose; the client refetches it for a
- new connection or a reconnect, and it is checked only at the handshake, so a
- film already playing is never interrupted by its expiry.
+ membership, `aud=MNP_AUD` and the target node's key, so it authorises the
+ member to **that** node only and is refused by the hub API and by any other
+ node. Short-lived on purpose; the client refetches it for a new connection or
+ a reconnect, and it is checked only at the handshake, so a film already
+ playing is never interrupted by its expiry.
+
+ The hub does not verify the node key it is handed — binding the token to it
+ only *restricts* the token to whatever node holds that key, which is the one
+ the client is connecting to; a wrong key yields a token no node will accept.
"""
rows = await db.execute(
select(GroupMember.group_id).where(GroupMember.user_id == current_user.id))
group_ids = [gid for (gid,) in rows.all()]
return {
- "mnp_token": issue_mnp_token(current_user.id, groups=group_ids),
+ "mnp_token": issue_mnp_token(current_user.id, groups=group_ids,
+ node_pk=(body.node_pk if body else "")),
"expires_in": 900,
}