Skip to main content
InternalMarkdown

ADR 0021. NestJS Control Plane + ZITADEL Local POC (Google IdP)

Status​

Accepted.

Context​

PlayerPal needs a real local vertical: IAM, Google social login, Postgres identity graph, BFF sessions, and link-intent. The team's preferred backend split is:

  • NestJS for main business logic, CRUD, and the interactive control plane
  • Go for small, parallel, latency-sensitive data-plane workers later

ZITADEL publishes a NestJS PKCE example (https://zitadel.com/docs/sdk-examples/nestjs). Spec §35 suggested Go for the core API; that remains appropriate for ingestion workers, but the control plane aligns better with NestJS next to the React portals.

Open question #1 (Keycloak vs ZITADEL) needs a POC vehicle for local/dev.

Decision​

  1. Use ZITADEL as the local/dev IAM POC (Google configured as a ZITADEL IdP).
  2. Implement the PlayerPal control plane as NestJS (services/api): Auth Code + PKCE, HttpOnly session cookies, iss+sub → pp_plr_..., link-intent stubs. Follow ZITADEL's NestJS/PKCE pattern; request openid only.
  3. Keep PostgreSQL (playerpal-db) as the PlayerPal system of record for players, aliases, and links, separate from ZITADEL's DB. DDL ownership is TypeORM migrations in Nest (ADR 0023), not hand-maintained schema.sql.
  4. Reserve Go (services/worker) for future ingestion/loyalty workers.
  5. Never persist player email, name, or phone (ADR 0012). Strip prohibited claims before any PlayerPal write.
  6. Treat passkey, Google, and provisional PlayerPal ID as first-class portal entry points into hosted login (prompt=login / prompt=create). PlayerPal does not implement WebAuthn credential storage; RP-initiated logout clears the IAM session after the PlayerPal cookie.

Keycloak remains a valid alternative if the ZITADEL POC fails spec §23.2.

Amendment (ADR 0023 / 0024)​

Item 6 is superseded for the local player portal: password/passkey and social create are Nest-owned; a public-key mirror may be stored for discoverable passkeys; logout clears the PlayerPal cookie only. See ADR 0024. DDL path is TypeORM (ADR 0023).

Consequences​

  • Local runbook: infra/README.md (login policy, Google IdP id, passkeys).
  • Production IAM pick is still gated on the §23.2 checklist, not this ADR alone.
  • Data-plane scale path stays open for Go without rewriting NestJS identity CRUD.
  • Google OAuth redirect URI is ZITADEL /idps/callback, not the NestJS app.
  • Silent SSO off a leftover console session is not acceptable for the player portal.