From 9a483774e97f8612b00e3d92c4d5ebc00c21980a Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Fri, 14 Aug 2026 03:40:43 +0200 Subject: fix(client): refresh the token when the node says "not a member" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A member added to a group after they signed in was refused by the node, told "Not a member of this group", and had no way forward but to log out and back in. The hub bakes `groups` into the access token at login and never pushes updates, so the token said they were in nothing while the database said otherwise. This lands on every newly invited member, at their first action, and the message tells them the opposite of the truth — toto2 was a member of newdemo on the hub and read that they were not. The refusal now carries a code the client can act on (`not_a_member`) rather than prose it would have to string-match, and the SPA refreshes the access token once and retries. Refreshing re-reads membership from the database, so the retry succeeds. Once per mount: if a fresh token still says not a member, that is the truth and it gets shown. The SPA had stored a refresh token since Phase 8 and never used it. It does now. Found in a browser, doing the ordinary thing — the automated run never sees it, because e2e.py logs in after being added to the group. Tests: 233 node+common, including a handshake test that the refusal carries the code, and the full e2e run against the live deployment. Co-Authored-By: Claude Opus 5 --- packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'packages/meshbay-node') diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py index 416e84c..46dda64 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py +++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py @@ -356,7 +356,8 @@ class WebRTCPeerSession: except HandshakeError as refusal: # HandshakeError messages are authored to be peer-safe, unlike arbitrary # exception text (L3) — the client needs to know *why* it was refused. - self._send({"type": "error", "detail": str(refusal)}) + self._send({"type": "error", "detail": str(refusal), + "code": getattr(refusal, "code", "")}) self._audit_auth_failed(group_id, str(refusal)) return -- cgit v1.2.3