# 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](0024-nest-owned-portal-auth.md). DDL path is TypeORM
([ADR 0023](0023-typeorm-data-layer.md)).

## 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.
