Skip to content

Conversation

@Lavodan
Copy link
Contributor

@Lavodan Lavodan commented Oct 29, 2025

Description:

Fixes #344.
Creates a new hostToken upon lobby creation, saves it as a GameServer attribute and sends it to the host, who then stores it as a cookie. Afterwards, when the host starts the game, it sends the hostToken in the request's body. If the hostToken does not match, the lobby does not start.
The algorithm for creating the hostToken is very simple and not cryptographically secure. The requests also aren't secure exclusive. I figured this isn't a very sensitive piece of information so it doesn't have to be protected too hard.
This PR does nothing for validation of changing private lobby settings, as those are overwritten when the host starts the game anyways.
I couldn't figure out how to do cookies through Express (I think we lack a middleware, and it seems silly to get it for just a single purpose), so technically, the hostToken is sent twice in the request (in cookie and in body), but I feel like this is so minor it doesn't matter too much.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

Lavodan

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Walkthrough

Adds host-token support end-to-end: GameServer stores a generated 16‑char hex host token; Worker includes the token in create_game responses and requires it to start private games; client HostLobbyModal stores the token in a cookie, sends it when starting, and deletes it afterward. (50 words)

Changes

Cohort / File(s) Summary
Server — GameServer
src/server/GameServer.ts
Adds private `hostToken: string
Server — Worker / Game flow
src/server/Worker.ts
create_game now obtains hostToken from the Game and includes it in the JSON response; start_game requires hostToken in request body for private games and returns 403 on missing/mismatch.
Client — Host lobby UI
src/client/HostLobbyModal.ts
createLobby now returns { gameInfo, hostToken }; sets hostToken cookie (Max‑Age=6h, Path=/, SameSite=Strict, Secure if https); startGame reads cookie and POSTs { hostToken } to /start_game, then deletes the cookie; call sites updated to use lobby.gameInfo and lobby.hostToken.
Localization
resources/lang/en.json
Adds private_lobby.creation_error = "Could not create lobby." and adjusts surrounding JSON punctuation.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant HostModal as HostLobbyModal (client)
    participant Worker as Worker (HTTP)
    participant Game
    participant Server as GameServer

    rect rgb(235,245,255)
    Note over Client,HostModal: Create lobby
    Client->>HostModal: createLobby()
    HostModal->>Worker: POST /create_game
    Worker->>Game: new Game(...)
    Game->>Server: createHostToken()
    Server-->>Game: hostToken (16‑char hex)
    Game-->>Worker: gameInfo + hostToken
    Worker-->>HostModal: { gameInfo, hostToken }
    HostModal->>Client: set cookie (hostToken, Max‑Age=6h, Path=/, SameSite=Strict)
    end

    rect rgb(235,255,235)
    Note over Client,Worker: Start private game (host-only)
    Client->>HostModal: startGame()
    HostModal->>HostModal: read hostToken cookie
    HostModal->>Worker: POST /start_game { hostToken }
    Worker->>Game: getHostToken()
    alt tokens match
        Worker-->>HostModal: 200 OK
        HostModal->>Client: delete hostToken cookie
    else mismatch or missing
        Worker-->>HostModal: 403 Forbidden
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay extra attention to:
    • Authorization branching and early-return/error paths in src/server/Worker.ts.
    • API response shape change for create_game (clients now receive hostToken).
    • Cookie creation/deletion, SameSite and Secure handling in src/client/HostLobbyModal.ts.

Suggested labels

Feature - Backend, Feature - Frontend, Exploits and Security

Suggested reviewers

  • scottanderson
  • evanpelle

Poem

A tiny hex key tucked in a cookie jar,
Six hours it waits, a quiet guiding star.
Hold it when starting, or the gate stays closed,
Host-only doors keep the lobby enclosed. 🎮🔑

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Add hostToken generation and authentication" directly and clearly summarizes the main change in the pull request. It accurately reflects the primary objective of introducing a host token mechanism to control who can start private lobbies, and it uses specific terminology that conveys meaningful information about what was implemented. The title is concise, avoids vague terms, and would allow a teammate reviewing history to immediately understand the purpose of these changes.
Linked Issues Check ✅ Passed The pull request addresses the primary objective from issue #344 by adding server-side host token validation that prevents unauthorized users from launching private games through the start_game endpoint [#344]. The implementation generates a hostToken when a lobby is created and enforces validation on game start, ensuring possession of the lobby code alone no longer grants full host privileges. However, the PR explicitly does not address parameter modification validation for non-hosts, noting in the description that such modifications are overwritten by the host at game start anyway. Since the core issue reported was the ability to launch games without authorization, and that specific vulnerability is fixed, this addresses the main objectives from the linked issue.
Out of Scope Changes Check ✅ Passed All changes in the pull request are directly related to the hostToken generation and authentication objective. GameServer.ts adds token creation and storage, Worker.ts integrates token validation into the game start flow, HostLobbyModal.ts manages token lifecycle through cookies, and en.json provides the necessary error message translation. No refactoring, unrelated feature additions, or ancillary modifications are present that would fall outside the scope of implementing host token authentication.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/Worker.ts (1)

161-185: Authorization is broken and early returns hang requests—both server and client need fixes.

The issues are confirmed:

  • Route /api/start_game/:id has no :hostToken parameter, so req.params.hostToken is always undefined.
  • Early return statements at lines 165 and 172 send no response, hanging the request.
  • Client code at src/client/HostLobbyModal.ts:798-810 sends no hostToken in the body at all.

Apply server changes in src/server/Worker.ts (161–185):

+import { timingSafeEqual } from "node:crypto";
 
  app.post("/api/start_game/:id", async (req, res) => {
    log.info(`starting private lobby with id ${req.params.id}`);
    const game = gm.game(req.params.id);
    if (!game) {
-      return;
+      return res.status(404).json({ error: "Game not found" });
    }
    if (game.isPublic()) {
      const clientIP = req.ip || req.socket.remoteAddress || "unknown";
      log.info(
        `cannot start public game ${game.id}, game is public, ip: ${ipAnonymize(clientIP)}`,
      );
-      return;
+      return res.status(400).json({ error: "Cannot start public game" });
    }
-    if (
-      !("hostToken" in req.params) ||
-      req.params.hostToken !== game.getHostToken()
-    ) {
-      log.info("cannot start private game, requestor is not host");
-      res.status(403);
-      return;
-    }
+    const tokenSchema = z.object({
+      hostToken: z.string().regex(/^[A-F0-9]{16}$/),
+    });
+    const parsed = tokenSchema.safeParse(req.body);
+    if (!parsed.success) {
+      const error = z.prettifyError(parsed.error);
+      return res.status(400).json({ error });
+    }
+    const expected = game.getHostToken();
+    if (!expected) {
+      return res.status(409).json({ error: "Host token not set for this game" });
+    }
+    const suppliedBuf = Buffer.from(parsed.data.hostToken, "utf8");
+    const expectedBuf = Buffer.from(expected, "utf8");
+    if (
+      suppliedBuf.length !== expectedBuf.length ||
+      !timingSafeEqual(suppliedBuf, expectedBuf)
+    ) {
+      log.info("cannot start private game, requester is not host");
+      return res.status(403).json({ error: "Forbidden" });
+    }
    game.start();
    res.status(200).json({ success: true });
  });

Update client at src/client/HostLobbyModal.ts:798-810 to send the token in body. The fetch call needs a body with hostToken. Where does this.lobbyId or similar contain or reference the host token? Please confirm the token source so the client code can be corrected.

🧹 Nitpick comments (2)
src/server/Worker.ts (1)

151-153: Only include hostToken for private games; avoid spreading an object wrapper.

Return the token only for private lobbies and keep the response shape simple.

Apply:

-    const token = game.createHostToken();
-    const hostToken = { hostToken: token };
+    const hostToken = game.isPublic() ? undefined : game.createHostToken();
...
-    res.json({ ...game.gameInfo(), ...hostToken });
+    res.json(
+      hostToken ? { ...game.gameInfo(), hostToken } : game.gameInfo(),
+    );

This avoids leaking an unnecessary secret for public games and keeps the JSON compact.

Also applies to: 157-157

src/server/GameServer.ts (1)

145-148: Add an explicit return type and consider null‑clearing semantics.

Small polish and safety:

  • Add return type for clarity.
  • Consider clearing the token after it’s used (e.g., right after start()).

Apply:

-  public getHostToken() {
-    return this.hostToken ?? null;
-  }
+  public getHostToken(): string | null {
+    return this.hostToken ?? null;
+  }

If you decide to clear the token on start:

// At the end of start():
this.hostToken = null;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ecced3c and ee760fc.

📒 Files selected for processing (2)
  • src/server/GameServer.ts (2 hunks)
  • src/server/Worker.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/server/Worker.ts (1)
src/server/GameManager.ts (1)
  • game (24-26)
🪛 GitHub Check: 🔍 ESLint
src/server/GameServer.ts

[failure] 134-134:
'tokenLength' is assigned a value but never used.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (5)
src/client/HostLobbyModal.ts (3)

801-809: Cookie parsing: simplify and harden.

Consider a small helper to read cookies; reduces bugs and improves readability.

Example:

+function getCookie(name: string): string | null {
+  const m = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
+  return m ? decodeURIComponent(m[1]) : null;
+}
...
-    const cookies = document.cookie.split(";");
-    let hostToken = "";
-    for (let i = 0; i < cookies.length; i++) {
-      const cookie = cookies[i];
-      if (cookie.trim().startsWith("hostToken=")) {
-        hostToken = cookie.split("=")[1];
-        break;
-      }
-    }
+    const hostToken = getCookie("hostToken") ?? "";

870-873: Type shape consistency for createLobby response.

You re-wrap server data into { gameInfo, hostToken }. Good. If TypeScript ever complains at call sites (e.g., “property hostToken missing”), ensure this function’s return type is used by callers and consider validating the server payload.

Optional hardening:

+type CreateLobbyResponse = { gameInfo: GameInfo; hostToken: string };
+const CreateLobbySchema = z.object({
+  gameInfo: z.any(), // or stricter GameInfo schema if available
+  hostToken: z.string().min(1),
+});
...
-): Promise<{ gameInfo: GameInfo; hostToken: string }> {
+): Promise<CreateLobbyResponse> {
...
-    const data = await response.json();
+    const data = await response.json();
+    // If server returns flat { ...gameInfo, hostToken }, normalize here:
+    const normalized = { gameInfo: data, hostToken: data.hostToken };
+    // Optionally validate:
+    // CreateLobbySchema.parse(normalized);
-    return { gameInfo: data, hostToken: data.hostToken };
+    return normalized;

Also applies to: 896-896


60-63: Timer typings cross‑env.

For browser code, prefer ReturnType<typeof setInterval/setTimeout> to avoid NodeJS.Timeout vs number mismatches.

-  private playersInterval: NodeJS.Timeout | null = null;
+  private playersInterval: ReturnType<typeof setInterval> | null = null;
-  private botsUpdateTimer: number | null = null;
+  private botsUpdateTimer: ReturnType<typeof setTimeout> | null = null;
src/server/Worker.ts (2)

174-181: Validate request body and tighten responses.

Add schema validation for hostToken; keep responses explicit.

-    const hostToken = req.body.hostToken ?? "";
+    const body = z.object({ hostToken: z.string().min(1) }).safeParse(req.body);
+    if (!body.success) {
+      return res.status(400).json({ error: "Invalid body" });
+    }
+    const { hostToken } = body.data;
...
-      log.info(`cannot start private game ${game.id}, requestor is not host`);
-      res.status(403).json({ success: false });
-      return;
+      log.info(`cannot start private game ${game.id}, requester is not host`);
+      return res.status(403).json({ success: false });

Also consider responding 404 when game is missing:

-    if (!game) {
-      return;
-    }
+    if (!game) {
+      return res.status(404).json({ error: "Game not found" });
+    }

151-157: Response shape: return { gameInfo, hostToken } instead of flattening.

This avoids mixing concerns and matches the client’s natural usage.

-    const hostToken = { hostToken: game.createHostToken() };
...
-    res.json({ ...game.gameInfo(), ...hostToken });
+    const hostToken = game.createHostToken();
+    res.json({ gameInfo: game.gameInfo(), hostToken });

Please confirm no other clients depend on the flat shape.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee760fc and f9b4c1e.

📒 Files selected for processing (3)
  • src/client/HostLobbyModal.ts (4 hunks)
  • src/server/GameServer.ts (3 hunks)
  • src/server/Worker.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/GameServer.ts
🧰 Additional context used
🧬 Code graph analysis (2)
src/server/Worker.ts (1)
src/server/GameManager.ts (1)
  • game (24-26)
src/client/HostLobbyModal.ts (1)
src/core/Schemas.ts (1)
  • GameInfo (125-131)
🪛 Biome (2.1.2)
src/server/Worker.ts

[error] 178-178: expected , but instead found ;

Remove ;

(parse)

🪛 GitHub Actions: 🧪 CI
src/server/Worker.ts

[error] 178-178: SyntaxError: ')' expected while running 'npx prettier --check .' (likely missing closing parenthesis or semicolon in response. Add ); to properly close res.status(403).json({ success: false }).

🪛 GitHub Check: 🔍 ESLint
src/server/Worker.ts

[failure] 178-178:
Parsing error: ')' expected.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/client/HostLobbyModal.ts (2)

595-600: Fix cookie typo and add security attributes.

The cookie setting still has "Max-Axe" instead of "Max-Age", and is missing URL encoding and security flags (SameSite, Secure).

This issue was already flagged in a previous review at lines 597-600.


821-821: Cookie deletion needs matching attributes.

The cookie deletion is missing the Path, SameSite, and Secure attributes used when setting the cookie. This can cause the deletion to fail.

This issue was already flagged in a previous review at line 821.

🧹 Nitpick comments (1)
src/client/HostLobbyModal.ts (1)

818-818: Use object shorthand.

Simplify { hostToken: hostToken } to { hostToken }.

-        body: JSON.stringify({ hostToken: hostToken }),
+        body: JSON.stringify({ hostToken }),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9b4c1e and 32900db.

📒 Files selected for processing (1)
  • src/client/HostLobbyModal.ts (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/client/HostLobbyModal.ts (1)
src/core/Schemas.ts (1)
  • GameInfo (125-131)
🔇 Additional comments (1)
src/client/HostLobbyModal.ts (1)

868-899: API returns hostToken—no validation needed.

The backend endpoint (Worker.ts line 156) spreads hostToken into every successful response: res.json({ ...game.gameInfo(), ...hostToken }). The assumption in the client code is valid and safe.

@Lavodan Lavodan changed the title Add token host generation authentication Add hostToken generation and authentication Oct 29, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/Worker.ts (1)

160-173: Bug: early returns without HTTP response.

Both “game not found” and “game is public” paths return without sending a response; callers will hang. Return proper status codes.

-    if (!game) {
-      return;
-    }
+    if (!game) {
+      return res.status(404).json({ error: "Game not found" });
+    }
     if (game.isPublic()) {
       // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
       const clientIP = req.ip || req.socket.remoteAddress || "unknown";
-      log.info(
+      log.warn(
         `cannot start public game ${game.id}, game is public, ip: ${ipAnonymize(clientIP)}`,
       );
-      return;
+      return res.status(400).json({ error: "Cannot start a public game" });
     }
♻️ Duplicate comments (4)
src/server/Worker.ts (1)

178-179: Syntax fix acknowledged.

The missing parenthesis previously flagged here is now fixed; CI should pass this spot.

src/client/HostLobbyModal.ts (3)

801-810: Make cookie parsing robust and URL‑decode the value.

Handle values with “=” and decode.

-    const cookies = document.cookie.split(";");
-    let hostToken = "";
-    for (let i = 0; i < cookies.length; i++) {
-      const cookie = cookies[i];
-      if (cookie.trim().startsWith("hostToken=")) {
-        hostToken = cookie.split("=")[1];
-        break;
-      }
-    }
+    const cookies = document.cookie.split(";");
+    let hostToken = "";
+    for (const cookie of cookies) {
+      const trimmed = cookie.trim();
+      if (trimmed.startsWith("hostToken=")) {
+        hostToken = decodeURIComponent(trimmed.substring("hostToken=".length));
+        break;
+      }
+    }
+    if (!hostToken) {
+      console.warn("startGame aborted: missing hostToken cookie");
+      return;
+    }

595-600: Harden cookie: URL‑encode and add security attributes; remove stray comment.

Encode the token and set SameSite and Secure (when HTTPS). The inline “getting error…” comment should be removed.

-        const cookieDurationSec = 60 * 60 * 6; //Store cookie for max 6 hours
-        // getting error here from lobby.hostToken
-        document.cookie = `hostToken=${lobby.hostToken}; Max-Age=${cookieDurationSec}; Path=/`;
+        const cookieDurationSec = 60 * 60 * 6; // store cookie for max 6 hours
+        const secure = location.protocol === "https:" ? "; Secure" : "";
+        document.cookie = `hostToken=${encodeURIComponent(lobby.hostToken)}; Max-Age=${cookieDurationSec}; Path=/; SameSite=Strict${secure}`;

892-896: Do not log tokens (response includes hostToken).

console.log("Success:", data) prints the token. Remove or log only non‑sensitive fields.

-    const data = await response.json();
-    console.log("Success:", data);
-
-    return { gameInfo: data, hostToken: data.hostToken };
+    const data = await response.json();
+    // Optional: debug minimal info
+    // console.debug("Lobby created", { gameID: data.gameID });
+    return { gameInfo: data, hostToken: data.hostToken };
🧹 Nitpick comments (3)
src/server/Worker.ts (1)

174-183: Validate request body and align error shape.

Parse hostToken with Zod, reject missing/empty values, and keep error shape consistent across APIs. Optionally invalidate the host token after a successful start to prevent reuse.

+  const StartGameSchema = z.object({ hostToken: z.string().min(1) });
+  const parsed = StartGameSchema.safeParse(req.body);
+  if (!parsed.success) {
+    const error = z.prettifyError ? z.prettifyError(parsed.error) : "Invalid body";
+    return res.status(400).json({ error });
+  }
-  const hostToken = req.body.hostToken ?? "";
+  const { hostToken } = parsed.data;
   if (hostToken !== game.getHostToken()) {
-    log.info(`cannot start private game ${game.id}, requestor is not host`);
-    res.status(403).json({ success: false });
-    return;
+    log.warn(`cannot start private game ${game.id}, requestor is not host`);
+    return res.status(403).json({ error: "Forbidden" });
   }
   game.start();
-  res.status(200).json({ success: true });
+  // Optional: make token one-time. If supported:
+  // game.invalidateHostToken?.();
+  return res.status(200).json({ success: true });
src/client/HostLobbyModal.ts (2)

818-819: Tiny cleanup: shorthand property.

Use { hostToken } for brevity.

-        body: JSON.stringify({ hostToken: hostToken }),
+        body: JSON.stringify({ hostToken }),

851-854: Noisy console log in production path.

Logging full GameInfo each second is noisy and may include usernames. Consider removing or guard behind a debug flag.

-        console.log(`got game info response: ${JSON.stringify(data)}`);
+        // console.debug("got game info response");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32900db and 1ec4d93.

📒 Files selected for processing (2)
  • src/client/HostLobbyModal.ts (4 hunks)
  • src/server/Worker.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/server/Worker.ts (1)
src/server/GameManager.ts (1)
  • game (24-26)
src/client/HostLobbyModal.ts (1)
src/core/Schemas.ts (1)
  • GameInfo (125-131)
🔇 Additional comments (1)
src/client/HostLobbyModal.ts (1)

821-823: Good: delete cookie with matching attributes.

Deletion mirrors Path and SameSite and adds Secure when applicable.

Comment on lines 869 to 872
async function createLobby(
creatorClientID: string,
): Promise<{ gameInfo: GameInfo; hostToken: string }> {
const config = await getServerConfigFromClient();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Nice: typed return for createLobby.

Clear contract { gameInfo, hostToken } simplifies callers. Consider adding a unit/integration test to ensure server response always includes hostToken.


🏁 Script executed:

#!/bin/bash
# Quick check of all call sites for the new shape
rg -n --type=ts 'createLobby\(' -C2

Length of output: 568


🏁 Script executed:

#!/bin/bash
# Search more comprehensively for createLobby usage across all TypeScript files
rg -n 'createLobby' --type=ts -B1 -A3

Length of output: 633


🏁 Script executed:

#!/bin/bash
# Check if there are existing tests for HostLobbyModal or createLobby
fd -t f '\.(test|spec)\.' --type=f | xargs rg -l 'createLobby' 2>/dev/null || echo "No test files found with createLobby"

Length of output: 36


🏁 Script executed:

#!/bin/bash
# Extract the full implementation of createLobby to verify it always provides both fields
ast-grep --pattern $'async function createLobby($_) {
  $$$
}'

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Extract the full createLobby function implementation
sed -n '869,/^}/p' src/client/HostLobbyModal.ts | head -50

Length of output: 979


🏁 Script executed:

#!/bin/bash
# Look for how the response is built and returned in createLobby
rg -n --type=ts 'hostToken' src/client/HostLobbyModal.ts -B2 -A2

Length of output: 1446


Add tests to validate server response includes hostToken field.

The function promises hostToken: string in its return type, but the implementation at line 895 directly returns data.hostToken without verifying the server response includes it. If the server response is missing hostToken, the code will return undefined instead of the promised string, breaking the type contract. The call site at line 599 accesses lobby.hostToken directly in a cookie, assuming it always exists.

Add unit/integration tests that verify the server API endpoint always includes hostToken in its response payload.

🤖 Prompt for AI Agents
In src/client/HostLobbyModal.ts around lines 869 to 872, the createLobby
function promises a hostToken but returns data.hostToken without asserting it
exists; add tests that call the server API (unit or integration) and assert the
response payload always contains a non-empty string hostToken and that
createLobby throws or fails the test when hostToken is missing. Implement at
least one positive test (valid response includes hostToken string) and one
negative test (mock server response missing hostToken should cause createLobby
to reject or throw), and ensure the test framework mocks the HTTP response shape
used by createLobby so the call site’s assumption that lobby.hostToken exists is
validated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/client/HostLobbyModal.ts (2)

789-824: Validate hostToken presence before starting game.

If the cookie expired or was deleted, hostToken will be an empty string (line 803). The request will proceed and likely fail server-side without clear user feedback. Consider checking that the token exists before calling the API.

Add validation before the fetch:

     }
+    if (!hostToken) {
+      console.error("Host token missing - cannot start private lobby");
+      alert("Session expired. Please create a new lobby.");
+      return;
+    }
     const response = await fetch(

869-895: Validate hostToken at response time and update caller to handle null case.

Your review identifies a real bug. The code has a type-caller mismatch:

  • createLobby returns hostToken: string | null with no validation (line 895)
  • Caller at line 599 uses encodeURIComponent(lobby.hostToken) directly without checking for null
  • If the server omits hostToken, the code silently sets the cookie to hostToken=null (the string), which breaks authentication

Fix with validation + type change (preferred):

async function createLobby(
  creatorClientID: string,
-): Promise<{ gameInfo: GameInfo; hostToken: string | null }> {
+): Promise<{ gameInfo: GameInfo; hostToken: string }> {
   // ... fetch logic ...
   const data = await response.json();
   console.log("Success:", data);
 
+  if (!data.hostToken || typeof data.hostToken !== "string") {
+    throw new Error("Server returned invalid hostToken");
+  }
   return { gameInfo: data, hostToken: data.hostToken };

This approach is cleaner because:

  • You validate once at the source, not at every call site
  • Type system now prevents null
  • Errors are explicit, not silent cookie corruption
  • Caller at line 599 works safely without changes

Add tests to verify the server always returns valid hostToken in the response.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ec4d93 and c74226a.

📒 Files selected for processing (2)
  • src/client/HostLobbyModal.ts (4 hunks)
  • src/server/GameServer.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/GameServer.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-21T20:06:04.823Z
Learnt from: Saphereye
PR: openfrontio/OpenFrontIO#2233
File: src/client/HostLobbyModal.ts:891-891
Timestamp: 2025-10-21T20:06:04.823Z
Learning: For the HumansVsNations game mode in `src/client/HostLobbyModal.ts` and related files, the implementation strategy is to generate all nations and adjust their strength for balancing, rather than limiting lobby size based on the number of available nations on the map.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
PR: openfrontio/OpenFrontIO#1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/HostLobbyModal.ts
🧬 Code graph analysis (1)
src/client/HostLobbyModal.ts (1)
src/core/Schemas.ts (1)
  • GameInfo (125-131)
🪛 GitHub Actions: 🧪 CI
src/client/HostLobbyModal.ts

[error] 599-599: TS2345: Argument of type 'string | null' is not assignable to parameter of type 'string | number | boolean'. Type 'null' is not assignable to type 'string | number | boolean'.

🔇 Additional comments (1)
src/client/HostLobbyModal.ts (1)

801-810: Cookie parsing looks good.

The manual parsing correctly handles URL-encoded tokens and = characters within the value using slice(1).join("=") and decodeURIComponent.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/HostLobbyModal.ts (1)

869-895: Silent failure: validate server response includes hostToken.

Line 895 returns an empty string if data.hostToken is missing (?? operator), but the function signature promises hostToken: string. An empty string satisfies the type contract but isn't a valid token. This silent failure means the call site at line 593 will store an invalid cookie without realizing the server didn't provide a token.

Apply this diff to fail fast when the server doesn't return a token:

     const data = await response.json();
     console.log("Success:", data);

-    return { gameInfo: data, hostToken: data.hostToken ?? "" };
+    if (!data.hostToken || typeof data.hostToken !== "string") {
+      throw new Error("Server did not return a valid hostToken");
+    }
+    return { gameInfo: data, hostToken: data.hostToken };
♻️ Duplicate comments (2)
src/client/HostLobbyModal.ts (2)

800-810: Cookie parsing is fragile and missing URL decoding.

The manual parsing at line 807 uses cookie.split("=")[1], which breaks if the token contains = characters. It also lacks decodeURIComponent to match the encoding that should be applied when the cookie is set.

Apply this diff:

     // Parse cookies for hostToken
     const cookies = document.cookie.split(";");
     let hostToken = "";
-    for (let i = 0; i < cookies.length; i++) {
-      const cookie = cookies[i];
-      if (cookie.trim().startsWith("hostToken=")) {
-        hostToken = cookie.split("=")[1];
+    for (const cookie of cookies) {
+      const trimmed = cookie.trim();
+      if (trimmed.startsWith("hostToken=")) {
+        hostToken = decodeURIComponent(trimmed.split("=").slice(1).join("="));
         break;
       }
     }

593-600: Guard against null hostToken and fix cookie attributes.

The code has two critical issues:

  1. Type error: lobby.hostToken is typed as string | null (per the return type at line 871), but line 599 doesn't check for null before using it. If the server fails to return a token, this will cause a runtime error.

  2. Cookie security mismatch: Line 599 sets the cookie without URL encoding, SameSite, or conditional Secure flag, but line 822 includes these when deleting. This mismatch will cause the deletion to fail, leaving the token in the browser.

Apply this diff to fix both issues:

     createLobby(this.lobbyCreatorClientID)
       .then((lobby) => {
+        if (!lobby.hostToken) {
+          throw new Error("Server did not return hostToken for private lobby");
+        }
         this.lobbyId = lobby.gameInfo.gameID;
         // join lobby
         const cookieDurationSec = 60 * 60 * 6; //Store cookie for max 6 hours
-        // getting error here from lobby.hostToken
-        document.cookie = `hostToken=${lobby.hostToken}; Max-Age=${cookieDurationSec}; Path=/`;
+        const secure = location.protocol === "https:" ? "; Secure" : "";
+        document.cookie = `hostToken=${encodeURIComponent(lobby.hostToken)}; Max-Age=${cookieDurationSec}; Path=/; SameSite=Strict${secure}`;
       })
🧹 Nitpick comments (2)
src/client/HostLobbyModal.ts (2)

818-818: Use object shorthand.

Simplify { hostToken: hostToken } to { hostToken }.

-        body: JSON.stringify({ hostToken: hostToken }),
+        body: JSON.stringify({ hostToken }),

869-895: Add tests for server response contract.

The function assumes the server will include hostToken in the response, but there's no test coverage verifying this contract. Add unit or integration tests that validate:

  • Server response includes a non-empty hostToken string
  • Function throws when hostToken is missing or invalid
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c74226a and dc1950f.

📒 Files selected for processing (1)
  • src/client/HostLobbyModal.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
PR: openfrontio/OpenFrontIO#1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-10-21T20:06:04.823Z
Learnt from: Saphereye
PR: openfrontio/OpenFrontIO#2233
File: src/client/HostLobbyModal.ts:891-891
Timestamp: 2025-10-21T20:06:04.823Z
Learning: For the HumansVsNations game mode in `src/client/HostLobbyModal.ts` and related files, the implementation strategy is to generate all nations and adjust their strength for balancing, rather than limiting lobby size based on the number of available nations on the map.

Applied to files:

  • src/client/HostLobbyModal.ts
🧬 Code graph analysis (1)
src/client/HostLobbyModal.ts (1)
src/core/Schemas.ts (1)
  • GameInfo (125-131)
🔇 Additional comments (1)
src/client/HostLobbyModal.ts (1)

821-822: Cookie deletion looks correct.

The deletion properly includes matching attributes (Path, SameSite, conditional Secure) and uses Max-Age=0.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/HostLobbyModal.ts (1)

1-1: Fix code formatting.

The pipeline detected code style issues. Run Prettier to fix:

npx prettier --write src/client/HostLobbyModal.ts
🧹 Nitpick comments (3)
src/client/HostLobbyModal.ts (3)

804-813: Validate hostToken before sending request.

If the cookie is missing or expired, hostToken will be an empty string and still sent to the server. The server will likely reject it, but making this explicit on the client improves UX.

Add a check before the fetch:

     // Parse cookies for hostToken
     const cookies = document.cookie.split(";");
     let hostToken = "";
     for (const cookie of cookies) {
       const trimmed = cookie.trim();
       if (trimmed.startsWith("hostToken=")) {
         hostToken = decodeURIComponent(trimmed.split("=").slice(1).join("="));
         break;
       }
     }
+    
+    if (!hostToken) {
+      console.error("No hostToken found - cannot start game");
+      alert(translateText("errors.host_token_missing"));
+      return;
+    }
+    
     const response = await fetch(

821-821: Use shorthand property syntax.

-        body: JSON.stringify({ hostToken: hostToken }),
+        body: JSON.stringify({ hostToken }),

895-898: Consider validating server response shape.

The return statement assumes data includes hostToken, but if the server omits it, null is returned silently. While the null check at line 595 catches this later, validating here would fail faster and provide clearer error context.

Optional improvement:

     const data = await response.json();
     console.log("Success:", data);
 
+    if (typeof data.hostToken !== "string") {
+      throw new Error("Server response missing or invalid hostToken field");
+    }
+    
-    return { gameInfo: data, hostToken: data.hostToken ?? null };
+    return { gameInfo: data, hostToken: data.hostToken };

This makes the function's contract stricter (always returns string) and clarifies where validation fails.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc1950f and 0230678.

📒 Files selected for processing (1)
  • src/client/HostLobbyModal.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
PR: openfrontio/OpenFrontIO#1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-10-21T20:06:04.823Z
Learnt from: Saphereye
PR: openfrontio/OpenFrontIO#2233
File: src/client/HostLobbyModal.ts:891-891
Timestamp: 2025-10-21T20:06:04.823Z
Learning: For the HumansVsNations game mode in `src/client/HostLobbyModal.ts` and related files, the implementation strategy is to generate all nations and adjust their strength for balancing, rather than limiting lobby size based on the number of available nations on the map.

Applied to files:

  • src/client/HostLobbyModal.ts
🧬 Code graph analysis (1)
src/client/HostLobbyModal.ts (1)
src/core/Schemas.ts (1)
  • GameInfo (125-131)
🪛 GitHub Actions: 🧪 CI
src/client/HostLobbyModal.ts

[warning] 1-1: Code style issues found in the above file. Run Prettier with --write to fix.

🔇 Additional comments (3)
src/client/HostLobbyModal.ts (3)

598-598: LGTM!

Correctly accesses gameID from the nested gameInfo object after the return type change.


600-602: Cookie settings look good.

The cookie attributes (Max-Age, Path, SameSite, conditional Secure) are properly set. SameSite=Strict is appropriate for preventing CSRF, though if the app needs to work in cross-site contexts (e.g., embeds), consider SameSite=Lax instead.


872-874: Typed return contract is clear.

The explicit return type { gameInfo: GameInfo; hostToken: string | null } makes the contract clear. However, the past review comment about adding tests to validate the server response is still relevant.

Consider adding tests that:

  • Verify the server API endpoint includes hostToken in responses
  • Test the null check at line 595 triggers when server returns null
  • Mock various server response shapes to ensure type safety

This would prevent runtime issues if the server response format changes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/HostLobbyModal.ts (1)

1-1: Run prettier to fix formatting.

The pipeline detected code style issues. Please run:

npx prettier --write src/client/HostLobbyModal.ts
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 172b38a and 92b945e.

📒 Files selected for processing (2)
  • resources/lang/en.json (1 hunks)
  • src/client/HostLobbyModal.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-08-16T10:52:08.292Z
Learnt from: TheGiraffe3
PR: openfrontio/OpenFrontIO#884
File: resources/lang/en.json:456-461
Timestamp: 2025-08-16T10:52:08.292Z
Learning: In OpenFrontIO, translation files in resources/lang/*.json (except en.json) should not be updated in regular PRs. Only dedicated translation PRs titled "mls" and made by Aotumori should update non-English locale files. Regular PRs should only update en.json when adding or modifying translation keys.

Applied to files:

  • resources/lang/en.json
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
PR: openfrontio/OpenFrontIO#1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-10-21T20:06:04.823Z
Learnt from: Saphereye
PR: openfrontio/OpenFrontIO#2233
File: src/client/HostLobbyModal.ts:891-891
Timestamp: 2025-10-21T20:06:04.823Z
Learning: For the HumansVsNations game mode in `src/client/HostLobbyModal.ts` and related files, the implementation strategy is to generate all nations and adjust their strength for balancing, rather than limiting lobby size based on the number of available nations on the map.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-08-17T20:48:49.411Z
Learnt from: woodydrn
PR: openfrontio/OpenFrontIO#1836
File: src/client/Main.ts:482-482
Timestamp: 2025-08-17T20:48:49.411Z
Learning: In PR #1836, user woodydrn prefers to keep changes minimal and focused on the specific issue (clientID persistence) rather than refactoring redundant code in JoinLobbyEvent dispatchers. They want to avoid scope creep in focused bug fix PRs.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-08-12T00:31:50.144Z
Learnt from: scottanderson
PR: openfrontio/OpenFrontIO#1752
File: src/core/game/Game.ts:750-752
Timestamp: 2025-08-12T00:31:50.144Z
Learning: In the OpenFrontIO codebase, changes to the PlayerInteraction interface (like adding canDonateGold and canDonateTroops flags) do not require corresponding updates to src/core/Schemas.ts or server serialization code.

Applied to files:

  • src/client/HostLobbyModal.ts
🧬 Code graph analysis (1)
src/client/HostLobbyModal.ts (2)
src/client/Utils.ts (1)
  • translateText (92-147)
src/core/Schemas.ts (1)
  • GameInfo (125-131)
🪛 GitHub Actions: 🧪 CI
src/client/HostLobbyModal.ts

[warning] 1-1: Code style issues found in the file. Run 'npx prettier --write' to fix.

🔇 Additional comments (5)
resources/lang/en.json (1)

219-220: LGTM! Translation key added correctly.

The new creation_error key provides user feedback when lobby creation fails, and the comma fix maintains valid JSON syntax.

src/client/HostLobbyModal.ts (4)

595-602: Good: null check and secure cookie attributes.

The guard prevents null from reaching encodeURIComponent, and the cookie now includes proper Max-Age, Path, SameSite, and conditional Secure flags.


812-821: Cookie parsing handles edge cases correctly.

The implementation properly URL-decodes the token and handles = characters within the value using slice(1).join("=").


832-833: Cookie deletion uses matching attributes.

The deletion correctly uses Max-Age=0 with the same Path, SameSite, and Secure flags as creation.


880-882: Clear type contract with null handling.

The function signature explicitly returns string | null for hostToken, and the implementation uses ?? to coalesce undefined to null, matching the contract.

Also applies to: 906-906

headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ hostToken: hostToken }),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Validate hostToken before sending.

If the cookie is missing, hostToken will be an empty string. The server might reject it, but there's no user feedback or validation here.

Consider validating and using shorthand syntax:

+    if (!hostToken) {
+      console.error("No hostToken found in cookies");
+      alert(translateText("errors.missing_host_token"));
+      return;
+    }
     const response = await fetch(
       `${window.location.origin}/${config.workerPath(this.lobbyId)}/api/start_game/${this.lobbyId}`,
       {
         method: "POST",
         headers: {
           "Content-Type": "application/json",
         },
-        body: JSON.stringify({ hostToken: hostToken }),
+        body: JSON.stringify({ hostToken }),
       },
     );

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/client/HostLobbyModal.ts (1)

616-623: Remove popup from DOM to prevent memory leak.

Each failed lobby creation appends a new div to document.body that is never removed. On repeated failures, orphan elements accumulate.

Add cleanup logic:

   .catch((err) => {
     console.error(`Failed to create lobby: ${err}`);
     const popup = document.createElement("div");
-    popup.className = "setting-popup"; // TODO: Change to general popup class?
+    popup.className = "error-popup";
     popup.textContent = translateText("private_lobby.creation_error");
     document.body.appendChild(popup);
+    setTimeout(() => {
+      popup.remove();
+    }, 3000);
     this.close();
   });

Alternatively, use an existing toast/notification system if available in the codebase.

🧹 Nitpick comments (1)
src/client/HostLobbyModal.ts (1)

829-829: Validate hostToken before sending.

If the cookie is missing, hostToken will be an empty string. The server will reject it, but the user sees no feedback until after the network round-trip.

Add validation and use shorthand syntax:

+    if (!hostToken) {
+      console.error("No hostToken found in cookies");
+      alert(translateText("errors.missing_host_token"));
+      return;
+    }
     const response = await fetch(
       `${window.location.origin}/${config.workerPath(this.lobbyId)}/api/start_game/${this.lobbyId}`,
       {
         method: "POST",
         headers: {
           "Content-Type": "application/json",
         },
-        body: JSON.stringify({ hostToken: hostToken }),
+        body: JSON.stringify({ hostToken }),
       },
     );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92b945e and 6d890d1.

📒 Files selected for processing (1)
  • src/client/HostLobbyModal.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
PR: openfrontio/OpenFrontIO#1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-10-21T20:06:04.823Z
Learnt from: Saphereye
PR: openfrontio/OpenFrontIO#2233
File: src/client/HostLobbyModal.ts:891-891
Timestamp: 2025-10-21T20:06:04.823Z
Learning: For the HumansVsNations game mode in `src/client/HostLobbyModal.ts` and related files, the implementation strategy is to generate all nations and adjust their strength for balancing, rather than limiting lobby size based on the number of available nations on the map.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-08-17T20:48:49.411Z
Learnt from: woodydrn
PR: openfrontio/OpenFrontIO#1836
File: src/client/Main.ts:482-482
Timestamp: 2025-08-17T20:48:49.411Z
Learning: In PR #1836, user woodydrn prefers to keep changes minimal and focused on the specific issue (clientID persistence) rather than refactoring redundant code in JoinLobbyEvent dispatchers. They want to avoid scope creep in focused bug fix PRs.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-08-12T00:31:50.144Z
Learnt from: scottanderson
PR: openfrontio/OpenFrontIO#1752
File: src/core/game/Game.ts:750-752
Timestamp: 2025-08-12T00:31:50.144Z
Learning: In the OpenFrontIO codebase, changes to the PlayerInteraction interface (like adding canDonateGold and canDonateTroops flags) do not require corresponding updates to src/core/Schemas.ts or server serialization code.

Applied to files:

  • src/client/HostLobbyModal.ts
🧬 Code graph analysis (1)
src/client/HostLobbyModal.ts (2)
src/client/Utils.ts (1)
  • translateText (92-147)
src/core/Schemas.ts (1)
  • GameInfo (125-131)
🔇 Additional comments (4)
src/client/HostLobbyModal.ts (4)

595-602: Nice: null check prevents type error.

The guard correctly narrows the type and ensures encodeURIComponent receives a string. Cookie attributes (Max-Age, Path, SameSite, Secure) are properly set.


812-821: LGTM!

Cookie parsing correctly handles tokens containing "=" characters and applies URL decoding.


832-833: LGTM!

Cookie deletion uses matching attributes (Path, SameSite, Secure) to ensure removal.


880-882: LGTM!

The function signature clearly promises both gameInfo and hostToken (nullable). The return statement correctly coalesces missing hostToken to null.

Also applies to: 906-906

@Lavodan Lavodan marked this pull request as ready for review October 30, 2025 09:14
@Lavodan Lavodan requested a review from a team as a code owner October 30, 2025 09:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API has no permission restrictions on private lobbies

1 participant