Matchmaking returns "Session not found" (401) for a valid JWT after a transient MQTT disconnect, with no recovery path
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 75/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Ít trao đổi
- Công nghệ
- typescript
- Lĩnh vực
- api, authentication, backend
Hướng nghiên cứu
Bắt đầu trong src/features/matchmaking/matchmaking.route.ts tại lần tra cứu getSession nghiêm ngặt, sau đó so sánh với ensureSession trong src/features/auth/auth.service.ts. Tái hiện request thiếu session được mô tả trong issue và xác minh rằng JWT hợp lệ có thể khôi phục session được DB hỗ trợ thay vì nhận Session not found (401).
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Matchmaking returns "Session not found" (401) for a player with a valid JWT, with no recovery path
Summary
A player who is correctly authenticated (valid JWT) can be permanently locked out of matchmaking with Session not found (401) after a single transient MQTT disconnect. Retrying does not help, because nothing recreates the in-memory session and the still-valid JWT means the client never re-authenticates. The only workaround is a full client restart.
Root cause
The in-memory session and the stateless JWT have decoupled lifetimes, and the matchmaking route is the only flow that requires the session strictly instead of self-healing.
-
Sessions are in-memory only.
src/state/index.ts—export const sessions = new Map<string, PlayerSession>(). No persistence, no rehydration on startup. -
JWT auth never checks for a session.
src/middleware/authenticate.tsonly runsverifyJwt(token)and setsreq.player. A valid token passes even when the session is gone. -
A non-lobby disconnect reaps the session immediately.
src/features/emqx/emqx.route.ts:async function releasePlayerLobbyOrSession(clientid) { const session = getSession(clientid) if (!session) return leaveAllQueues(clientid) if (session.lobbyCode) { await startGracePeriod(clientid) // lobby members get a grace period } else { removeSession(clientid) // everyone else is removed on the spot } }The MQTT
clientidis theplayerId(the client sendsplayer_idas the MQTT client id), sogetSession(clientid)/removeSession(clientid)resolve the real session. A player who is matchmaking — and therefore not yet in a lobby — has their session deleted the instant EMQX reportsclient.disconnected. -
Matchmaking requires the session strictly.
src/features/matchmaking/matchmaking.route.ts:const session = getSession(req.player.playerId) if (!session) throw new AppError('Session not found', 401)By contrast, ~5 other endpoints call
ensureSession()(src/features/auth/auth.service.ts), which rebuilds the session from the DB when it's missing. Matchmaking is the asymmetric one that does not self-heal.
Reproduction
- Authenticate (Steam) — session created, MQTT connected.
- Drop the MQTT connection briefly (ordinary network blip). The client default is
reconnect = false, so it does not auto-reconnect. - EMQX fires
client.disconnected; since the player isn't in a lobby, the server callsremoveSessionimmediately. - Click matchmake →
POST /api/matchmaking/queuewith the still-valid JWT →getSessionreturns nothing →Session not found(401). - Retrying never recovers: the JWT is still valid, so the client doesn't re-auth, and nothing else recreates the session.
Impact
A single transient disconnect strands an authenticated player. From the user's side it looks like matchmaking is simply broken; the only fix they have is to fully quit and relaunch the game (which forces a fresh /auth and recreates the session).
Suggested fixes (in order of smallest blast radius)
- Make matchmaking self-heal like the other routes — use
ensureSession(req.player.playerId)instead of strictgetSession. A DB-backed (Steam-authed) player would transparently get their session rebuilt instead of a 401. This is the minimal, consistent fix. - Don't immediately reap non-lobby sessions on disconnect — give them the same grace period lobby members get, so a brief blip doesn't destroy the session.
- (Client-side, optional) Treat a
401 Session not foundas "re-authenticate, then retry" rather than surfacing it as a terminal error.
Notes / unverified
The mechanism above is confirmed against the code end-to-end (in-memory map, stateless JWT, immediate non-lobby reap, strict matchmaking lookup, clientid == playerId). What is not independently confirmed is that a given user report was caused by this exact path versus another session-loss trigger (e.g. a server restart/redeploy wiping the in-memory map, or a grace-period expiry). All of those funnel into the same end state — valid JWT, no session, strict 401 — so the fixes apply regardless.
- Ngôn ngữ chính
- TypeScript
- Star
- 13
- Fork
- 21
- Merge trung bình
- 3 ngày 16 giờ
- Pull request đã merge (30 ngày)
- 6
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của Balatro-Multiplayer/BalatroMultiplayerAPI-Server
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 72/100
-
In Game Telemetry Đang mở
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 20/100
Tất cả issue của Balatro-Multiplayer/BalatroMultiplayerAPI-Server
Issue tương tự
-
VerificationGate: ATTRIBUTION quote guard never matches a normal quotation (\b around the quote) Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
danielmiessler/LifeOS#2234 ·
-
T: Bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 85/100
-
Mend: dependency security vulnerability untriaged
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100