SCRUM-258 — AdminUI onboarding and TOTP enrollment screens¶
Plan ref: UI-13 (docs/11-admin-plane-plan.md). Needs the admin-auth fix in
SCRUM-258 (backend, PR #80) so onboarding answers with the MFA policy. Stacked on
SCRUM-257.
What exists¶
| Piece | Notes |
|---|---|
/onboard (public) |
the token comes from the URL fragment (/admin/onboard#token=…, never sent to a server), is removed from the address bar with replaceState at once, and is never stored; shows who is invited and at what role (or "reset your password"), the expiry; password + confirm with a strength meter (the server's policy message is authoritative); invalid/expired → "ask an admin for a new one" |
| Outcomes | tokens → signed in; mfa_required → the code screen (which also takes a recovery code); mfa_enrollment_required → enrollment. Login routes mfa_enrollment_required the same way |
/login/enroll |
explains why; QR code (qrcode, PNG with token colours), the secret grouped by four with a copy button, a 5-minute countdown, code → confirm |
| Recovery codes | ten codes, Copy all, Download .txt, "I have saved these codes" required before Continue |
| Dependencies | qrcode 1.5.4 + @types/qrcode (lockfile hand-merged), pre-bundled in dev |
Known limit: after confirm the session exists, so the app shell is already visible behind the recovery-codes screen; the "saved" checkbox is a nudge, not a hard gate.
How to verify¶
cd services/adminui
npm run typecheck && npm run lint && npm run format:check && npx vitest run && npm run build
npx playwright test
/admin/onboard#token=mock-admin-invite (admin → enrollment; secret
JBSWY3DPEHPK3PXP, code 123456), mock-liveops-invite, mock-expired.
Results at time of writing¶
- typecheck, lint, prettier, vitest (468 tests), build: pass; Playwright 39/39 on the first full run, onboarding spec 4/4 on its own (full back-to-back runs still show the suite's random 30 s timeouts, SCRUM-261). Screenshots checked for all three screens.
How it was built¶
DeepSeek run scoped (Landlock) to services/adminui (625 s, ~56k output tokens,
reasoning effort low); Claude added qrcode beforehand. Claude review: token handling
(fragment only, stripped, never stored) checked; while preparing this ticket Claude found
the onboarding MFA bypass fixed in the backend part.