diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/revocation.py')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/api/revocation.py | 13 |
1 files changed, 12 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/revocation.py b/packages/meshbay-hub/src/meshbay_hub/api/revocation.py index 60b0c88..1f1f5c5 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/revocation.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/revocation.py @@ -56,6 +56,11 @@ _connected_nodes: dict[str, WebSocket] = {} # node_id → websocket _node_groups: dict[str, list[str]] = {} # node_id → [group_id, ...] _punch_events: dict[str, asyncio.Event] = {} # node_id → signaling event +# How long an unauthenticated socket may stay open before saying who it is. The +# node sends its auth message the moment the connection opens; anything that +# has not spoken by now is holding a socket and a task for nothing. +NODE_WS_AUTH_TIMEOUT = 10.0 + def is_node_connected(node_id: str) -> bool: return node_id in _connected_nodes @@ -336,7 +341,13 @@ async def node_websocket(ws: WebSocket): try: # Auth: expect {"type": "auth", "token": "<jwt>", "node_id": "..."} - raw = await ws.receive_text() + # Bounded: the socket is accepted before anyone is authenticated, so an + # unbounded wait is a connection any stranger can hold open for ever. + try: + raw = await asyncio.wait_for(ws.receive_text(), NODE_WS_AUTH_TIMEOUT) + except TimeoutError: + await _reject(ws, "Authentication timed out", 4001) + return msg = json.loads(raw) if msg.get("type") != "auth" or "token" not in msg: await _reject(ws, "Send auth first", 4001) |