mancala/TODO.md
Halit Aksoy d52960fa0a feat(keycloak): add a login theme in the game's look
The mancala login theme extends keycloak.v2 with one stylesheet: the game's
app bar and logo, the board's colors, pit-shaped inputs, pill buttons and a
board with stones on top of the card. Dark colors follow the system color
scheme. The realm turns the theme on and offers English and Turkish.

The test container now loads the theme and checks that the login page uses
it in the requested language.
2026-10-16 01:13:20 +03:00

4.6 KiB

TODO

Auth

Mobile login with Keycloak (phase 5)

The web app logs in with Keycloak; the mobile app still uses the legacy ?userKey= login.

  • Add a mancala-mobile public client to apps/keycloak/realm/mancala-realm.json (PKCE S256, redirect com.mancala_mobile://callback) and to KEYCLOAK_CLIENT_IDS for the backend
  • Add react-native-app-auth with the redirect scheme set up for Android and iOS
  • Log in as a guest on first launch (guest=true), with offline_access; keep tokens in MMKV
  • Connect with rtmt.connectWebSocket(url, getToken) instead of ?userKey= (apps/mobile/src/App.tsx) and call reauthenticate after refreshing the token
  • Log in / sign up / log out in the app, player names on the game screen (getPlayerDisplayName in core)
  • Decide whether guests may skip the browser tab on first launch (would need a custom grant type in the Keycloak extension)
  • Delete the unused copy of the WebSocket client in apps/mobile/src/rtmt
  • Test on a device

Game history on mobile

The web app has game history (see docs/specs/game-history); the mobile app needs Keycloak login first.

  • Ongoing-game banner, live and popular games, recent games on the home screen
  • Move list and looking back during a live game; replay of finished games

After the mobile release

  • Turn off LEGACY_USERKEY_AUTH once old app versions are no longer in use; this also removes /register

Later

  • Let guests turn their guest user into an account without changing identity (Keycloak required action)
  • Statistics for logged in users (wins, losses), now that games are saved
  • Social logins (Keycloak identity providers)
  • Keycloak email theme in the game's look (password reset emails still use Keycloak's)
  • Login pages follow the system color scheme; they could follow the theme chosen in the app instead
  • Restrict the Keycloak admin console in production

Game history

  • A page with all of a user's games (the home page shows the last 3)
  • Show games of deleted accounts differently, once accounts can be deleted
  • Live game state, previews and spectators assume a single backend instance; share them (e.g. Valkey pub/sub) before running more than one
  • If Valkey is unreachable when the backend starts, live games are not restored until the next restart

Deployment

  • Build the backend and frontend images from the repo root in CI: docker build -f apps/backend/Dockerfile . and docker build -f apps/frontend/Dockerfile .
  • Pass --build-arg KEYCLOAK_URL=https://auth.playmancala.app when building the frontend image
  • Deploy Keycloak (apps/.env with KEYCLOAK_DB_PASSWORD, KEYCLOAK_ADMIN_PASSWORD, KEYCLOAK_HOSTNAME=https://auth.playmancala.app) and reload nginx with the new auth.playmancala.app block
  • Deploy the backend before the web app; the new web app needs token auth on the server
  • Back up the Postgres volume (Keycloak and saved games), e.g. a daily pg_dumpall
  • Set MANCALA_DB_PASSWORD and POSTGRES_PASSWORD in apps/.env on the server; the postgres volume replaces keycloak-db, so Keycloak starts with a fresh database there

Security

  • Move the Android signing passwords out of apps/mobile/release_android.sh (committed in bb1c750) into ~/.gradle/gradle.properties or environment variables, and consider resetting the upload key
  • Add apps/mobile/mancala-upload-key.jks to .gitignore (only android/app/mancala-upload-key.jks is ignored)
  • Validate moves before running them instead of treating every invalid move as a crash: each one writes a file to crashes/ and is broadcast to the players (GameManager.onGameError)

Bugs

  • PitAnimator.animatePit: previousPitIndex > 0 should be >= 0; a one-stone move from pit 0 does not empty that pit during the animation
  • PitAnimator.dispose removes a newly bound function, so the theme listener is never removed
  • Sowing puts stones into the opponent's store; check against the intended Mangala rules and add a test
  • onGameError broadcasts an Error, which serializes to {}

Maintenance

  • The web and mobile apps duplicate components and stores; move shared code into packages/core
  • Mixed lockfiles: packages/* and apps/mobile have both yarn.lock and package-lock.json
  • Align TypeScript and Jest versions across packages
  • Tests for MatchMaker and PitAnimator
  • CI that runs the tests
  • Pre-existing type errors: 4 in the frontend, 11 in mobile; the frontend has no tsconfig.json, so nothing type-checks it