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
- 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 communityzitadel-login-uias the product. - Keep Nest as BFF (Auth Code + PKCE,
openidonly,iss+sub→ player). The Login app stays on the same public origin as the ZITADEL API (POC: Traefik/ui/v2/loginonlocalhost:8080). PlayerPal does not store WebAuthn credentials. - v1 scope: one fork, one deploy, player-portal look. Per-portal skins and method sets come later; do not split merge trains yet.
- Community login UI may inform layout and polish only. Service principal
remains
IAM_LOGIN_CLIENT, notIAM_OWNER. - 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 bindsiss+subto that row, assurance starts at A0 (ADR 0020) until a stronger proof is added. No email verification on the player path;EMAIL_VERIFICATIONstays off for players. - 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-loginwith the forked image; upstream sync of login +@zitadel/client/ proto is an ongoing cost (see ZITADEL fork-and-deploy docs). docs/authentication.mdand the local runbook should describe the forked Login entry, player-portal chrome, and pre-mintedpp_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.