Add reusable two-factor verification for login and money operations (#32)

This commit is contained in:
Stephan D committed 2026-09-12 12:44:34 +02:00
1 parent b7a58b1a87
commit fb40f860d2
41 files changed
+2439 -43

No files matched your search

+37
View File
@@ -321,3 +321,40 @@ above describe delivery-time state and are superseded by this integration record
Validation of the combined tree: all 115 frontend tests and app analysis passed;
diff checks passed. Branches were retained. No checks were bypassed.
## Reusable two-factor verification — task #32
Branch `two-factor-32` adds a SXCHNGE-designed, theme-based code page reused by
login, top-up, exchange, payout and verified profile enable/disable. EN/RU copy,
one accessible input, keyboard/paste support, resend cooldown, invalid/expired/
locked/unavailable recovery and cancellation preserve context. Transaction
verification retains its reviewed funds movement and fees. No Sendico UI or code
was copied for this feature.
Shared auth owns explicit DTOs, mappers, domain models, service contracts and
providers. Method-aware contracts distinguish email and authenticator challenges;
the initial email fixture uses code `123456`, sends no email, defaults off and
resets on reload. Real TOTP enrollment, method selection, delivery, recovery and
backend enforcement remain integration work. Mobile can reuse the page and
providers with its own router/localization and service adapters.
Login stays pending until approval is consumed. The dummy wallet consumes a
purpose/quote-bound, expiring, one-use approval before execution. Concurrent
idempotent retries debit once; expired quotes require fresh review; uncertain
outcomes retain reconciliation. Timers stop on route/app disposal and late
responses are ignored. Settings update only after service confirmation. Analytics
retains parent journey correlation and distinguishes verification from actual
login/settings/settlement outcomes; codes, grants and targets never enter events.
Frontend and UX review covered the complete task diff. Validation passed:
`dart format --output=none --set-exit-if-changed` for all 34 changed Dart files;
`flutter analyze` in app, sxchnge_auth, sxchnge_wallet and sxchnge_analytics;
`make test-frontend` (147 tests); `flutter build web` (including Wasm dry run);
`git diff --check`. Browser checks at 1280×720 and 390×844 verified themed layout,
invalid-code recovery, keyboard entry and confirmed settings. Widget tests cover
Russian at 320px/1.6× text scale and all money entry points. No security scan was
requested or run. Earlier validation failures were corrected, including ARB
metadata handling, disabled-policy latency, route timer disposal and lint findings.
See [the UX and measurement contract](ux/two-factor.md) and
[shared auth integration guide](../frontend/packages/sxchnge_auth/README.md).
+68
View File
@@ -0,0 +1,68 @@
# Two-factor verification — task #32
Use a SXCHNGE-designed challenge page: themed security icon, clear purpose,
operation summary when moving money, one labeled six-digit input, Verify and
continue, contextual delivery recovery and Cancel. Avoid six separate focus
boxes, automatic submission, repeated banners and raw service error text.
Email is the initial service fixture. Method is carried in settings and challenge
DTO/model contracts. Authenticator/TOTP challenges use their own instruction and
have no email target or resend controls. The reusable shared page accepts labels
and an optional summary, so clients supply their own localization and context.
Authenticator enrollment/secret provisioning is a future backend capability;
it is not offered as a working setting without that contract. No Sendico UI or
code is copied. Its pending-login concept informed the separation of steps.
Profile shows current protection status and verified enable/disable actions.
Cancel changes nothing. No optimistic protection toggle: the service result
must confirm the new setting. The existing stub has one account and resets on
reload. Production settings must be account-scoped and authenticated.
Login first validates credentials. A pending result is not a session or login
success; complete it only after consuming a matching approval. Clear the password
before opening the challenge. Cancel returns to login without successful state.
All top-ups, exchanges and payouts request a factor when service policy requires
it. The challenge binds to the quote ID; its one-use grant is checked by the
operation adapter before money moves. Editing, expired quotes, mismatched grants
and reuse cannot authorize a different transaction. Existing idempotency and
unknown-outcome reconciliation remain authoritative. Cancel returns to the review
with the draft preserved. Expired quotes require fresh review and confirmation.
Invalid code preserves context; retry stays in the same challenge. Email resends
have service-owned cooldowns and replace the previous challenge. Expired code,
rate limiting and unavailable service have localized recovery. Verification and
resend disable duplicate actions, clean up timers/controllers on disposal, and
ignore late results. Pasted codes, keyboard submission, live errors and EN/RU
mobile layouts must work.
Measurement extends existing typed journeys with factor delivery/verification and
settings outcome steps. Login completion waits for the final login result;
transaction completion still waits for settlement. Cancelled challenges do not
claim success. No code, email target, challenge ID, grant, secret, amount or
recipient enters analytics or route URLs. Events retain the simulated flag.
Actual MFA enforcement, OTP delivery/validation, enrollment and recovery must run
on the backend before production use.
## Measurement and review evidence
- The existing login/transaction attempt owns verification and retries. A factor
success is an intermediate step; only completed login, confirmed settings save
or transaction settlement can complete its parent journey.
- `factor_challenge` measures settings/transaction policy checks and challenge
creation failures. `factor_delivery` measures email resends;
`factor_verification` measures code checks and explicit cancellation;
`factor_settings` measures the confirmed setting update. Login's existing
credential step includes initial challenge creation and its failures.
- Product/support can compare failure and resend rates by typed step, and measure
completion/duration per distinct parent journey. Retries remain in that journey;
its existing timeout/explicit-navigation rules close abandoned attempts. Pending
money operations continue through the existing settlement recorder.
- Browser review: desktop 1280×720 and mobile 390×844; theme roles, heading, input,
buttons, invalid-code recovery, keyboard submission and confirmed setting status.
Widget tests additionally cover Russian at 320px and 1.6× text scale, all money
entry points, cancellation, service errors and missing route context.
- Analytics tests check correlation, one terminal login outcome, cancellation,
challenge creation failure and absence of codes in event payloads. Stub policy
remains simulated even if a credentials adapter returns a non-simulated result.
Backend/cross-device outcomes remain unavailable until services are integrated.