Skip to main content
InternalMarkdown

ADR 0022. Official ZITADEL Login App Fork for Portal Sign-In

Status​

Accepted.

Context​

Portal sign-in must feel like PlayerPal, not stock ZITADEL. Console branding cannot change layout or hide email-centric paths enough for player rules (ADR 0012, ADR 0020, ADR 0021). Building a login UI from scratch against the Session API duplicates what ZITADEL already ships as MIT apps/login. The community zitadel-login-ui is useful as visual inspiration only: it is not an official product, documents IAM_OWNER, and is unverified against Login V2 finalize / OIDC proxy requirements.

All portals (player, staff, operator, provider, affiliate) will eventually need a custom login look. Starting with five skins or five deploys is premature while only the player portal has real Nest + ZITADEL auth.

Password-on-first-signup conflicts with a naive reading of ADR 0020: a pp_plr_... does not exist before registration, and player-chosen usernames are out. Cold create still needs a non-email loginname in IAM.

Decision​

  1. Customize portal login by forking the official ZITADEL Login V2 app (apps/login), not by console-only branding, not by build-your-own Session UI, and not by adopting community zitadel-login-ui as the product.
  2. Keep Nest as BFF (Auth Code + PKCE, openid only, iss+sub → player). The Login app stays on the same public origin as the ZITADEL API (POC: Traefik /ui/v2/login on localhost:8080). PlayerPal does not store WebAuthn credentials.
  3. v1 scope: one fork, one deploy, player-portal look. Per-portal skins and method sets come later; do not split merge trains yet.
  4. Community login UI may inform layout and polish only. Service principal remains IAM_LOGIN_CLIENT, not IAM_OWNER.
  5. Player create on the fork must support passkey, Google, and provisional PlayerPal ID + password on the first signup screen. For password create: PlayerPal pre-mints pp_plr_..., IAM uses that value as the loginname (never email, never a player-chosen username), Nest binds iss+sub to that row, assurance starts at A0 (ADR 0020) until a stronger proof is added. No email verification on the player path; EMAIL_VERIFICATION stays off for players.
  6. ZITADEL + this fork remain contingent on ADR 0021 / §23.2. Kill the fork path if the IAM checklist fails, player flows cannot stay email-free, or upstream Login merges cost more than we will budget (default signal: about five engineer-days per quarter). Local passkeys are disposable; durable enrollments only on the permanent auth host.

Consequences​

  • Infra will replace or overlay ghcr.io/zitadel/zitadel-login with the forked image; upstream sync of login + @zitadel/client / proto is an ongoing cost (see ZITADEL fork-and-deploy docs).
  • docs/authentication.md and the local runbook should describe the forked Login entry, player-portal chrome, and pre-minted pp_plr_... password create.
  • Later per-portal looks should prefer one fork / one merge train with client- or path-selected themes, not independent forks.
  • Open question “whether PlayerPal ID + password is offered” is yes for player portal signup, under ADR 0020 provisional rules plus pre-mint here.
  • Does not by itself close production IAM vendor choice (§23.2).

Amendment (ADR 0024)​

For the local player portal, password create/sign-in and passkey ceremonies are Nest Session/User API paths, not “on the Login V2 first screen.” Nest may store a WebAuthn public-key mirror for discoverable login. The Login V2 fork remains optional for skins/experiments; authoritative player password/passkey UX is ADR 0024.