mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-20 15:48:33 +02:00
refactor: unify identity resolution on stable user_id (anon_token)
There were two identity systems: SessionManager keyed on the stable, UNIQUE anon_token, and getOrCreateUser keyed on the mutable, non-unique socket_id. The latter could create a duplicate "orphan" user row (or resolve the wrong row) when the socket_id lookup raced the session's socket_id update — desyncing credits and payouts between the two rows for one human. - getOrCreateUser now resolves through SessionManager's stable identity first (anon_token -> users.id), re-selecting a fresh row by stable id, and only falls back to the legacy socket_id lookup when no session exists. The last-resort create now warns, since it indicates a missing session. - gameModeManager.sessionManager is wired in SocketHandlers so the session-first path is active in production. - anon_token is already UNIQUE (migration 003), so the stable-identity constraint already exists; no new migration needed. Re-keying the in-memory activeGames map by user_id is deferred (invasive; already mitigated by stamping dbUserId on the game in the disconnect fix). Adds identityResolution.test.js proving no duplicate row is created when a session exists. Full suite: 17 suites / 136 tests.
This commit is contained in:
@@ -33,7 +33,7 @@ Key: `S` ≤ half day · `M` ~1–3 days · `L` ~1 week+ · 🔴 launch-blocker
|
||||
- [x] **1.6 S — `credits` → BIGINT; idempotent 010; atomic migration runner.** ✅ Migration 018 widens `users.credits` + `credit_transactions.amount/balance_after` to BIGINT. Migration 010 rewritten with conname-guarded `DO` blocks (re-runnable, no startup wedge). Runner now applies each migration **and** records the ledger row in one transaction (no applied-but-unrecorded state). Verified end-to-end: all 18 migrations applied cleanly to a throwaway Postgres DB, re-run skipped via the ledger, `credits` confirmed `bigint`, all CHECKs present.
|
||||
|
||||
## Phase 2 — Identity, abuse & concurrency (🟠)
|
||||
- [ ] **2.1 L — Unify identity on `user_id`.** One `IdentityService.resolve(socket)`; kill `getOrCreateUser(socketId)` keying (`gameModeManager.js:926`); key `activeGames` by `user_id`.
|
||||
- [x] **2.1 L — Unify identity resolution on stable `user_id`.** ✅ `getOrCreateUser` now resolves through SessionManager's stable `anon_token`→`id` identity first (re-selecting a fresh row by stable id), falling back to the legacy `socket_id` lookup only when no session exists — eliminating the duplicate "orphan" user row that caused credit/payout desync. `gameModeManager.sessionManager` wired in SocketHandlers. `anon_token` is already `UNIQUE` (migration 003), so the stable-identity constraint exists. New `identityResolution.test.js` proves no duplicate row is created when a session exists. _Deferred: re-keying the in-memory `activeGames` map by `user_id` (invasive; already mitigated in 0.5 by stamping `dbUserId` on the game) — tracked as a follow-up._
|
||||
- [ ] **2.2 M — Reconnect-proof rate limiting.** Key on `user_id`/IP; IP-limit chat; set `trust proxy`; de-dup limit configs (`src/network/rateLimiter.js:80`).
|
||||
- [ ] **2.3 M — Input validation + leak fixes.** Validate socket payloads (`connectionHandler.js:90`); TTL-evict `mempoolNotified` (`socketHandlers.js:223`); evict `sessions` on disconnect; wire all `dispose()` into `shutdown()`; snapshot in `checkGamesTimeout` (`socketHandlers.js:1012`).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user