feat: guest play — allow singleplayer quiz without auth

- Add optionalAuth middleware: attaches session when present,
  never blocks (guests pass through)
- Make game endpoints (start/answer) accept optional auth
- GameSessionStore.userId: string → string | null
- Rate limiter falls back to IP for unauthenticated users
- Frontend: remove /play route guard, show 'Create account' CTA
  on score screen for guests
- Add tests for guest session creation, answer submission,
  and cross-user session isolation
This commit is contained in:
lila 2026-05-31 21:28:08 +02:00
parent d55a1ed648
commit 0118798e36
11 changed files with 298 additions and 32 deletions

View file

@ -1,4 +1,4 @@
import rateLimit from "express-rate-limit";
import rateLimit, { ipKeyGenerator } from "express-rate-limit";
import type { Request } from "express";
// TODO: When Valkey is wired up, swap the default in-memory store for
@ -33,7 +33,8 @@ export const gameLimiter = rateLimit({
limit: 150,
standardHeaders: "draft-8",
legacyHeaders: false,
keyGenerator: (req: Request) => req.session!.user.id,
keyGenerator: (req: Request) =>
req.session?.user.id ?? ipKeyGenerator(req.ip ?? "unknown"),
message: {
success: false,
error: "Too many requests, please try again later.",
@ -45,7 +46,8 @@ export const lobbyLimiter = rateLimit({
limit: 20,
standardHeaders: "draft-8",
legacyHeaders: false,
keyGenerator: (req: Request) => req.session!.user.id,
keyGenerator: (req: Request) =>
req.session?.user.id ?? ipKeyGenerator(req.ip ?? "unknown"),
message: {
success: false,
error: "Too many requests, please try again later.",