152 lines
9.1 KiB
Markdown
152 lines
9.1 KiB
Markdown
# Authentication UX and analytics contract
|
|
|
|
Task: [#12](https://git.sendico.io/tech/sxchnge/issues/12). Applies to login,
|
|
signup through email verification, and standalone confirmation links.
|
|
|
|
## User outcomes and interface
|
|
|
|
Signup leads to email delivery, then confirmation. Sent status shows a fixed
|
|
recipient; Change email explicitly leaves that state and focuses the editor.
|
|
A temporary confirmation failure prioritizes Retry verification, with requesting
|
|
a replacement email secondary. Expired/invalid links offer replacement delivery.
|
|
Demo labels sit beside the heading, and demo outcomes explicitly state that no
|
|
email was delivered or account verified. The demo confirmation link is the
|
|
primary next step on the simulated sent screen. EN/RU use the installed theme.
|
|
|
|
## Measurement scope and attempts
|
|
|
|
Owner/consumer: the sxchnge product team, diagnosing onboarding completion and
|
|
choosing between fixes to registration, delivery and confirmation recovery.
|
|
|
|
A journey starts at the first **locally valid service request**. This intentionally
|
|
measures submitted attempts, not page views or field editing; pre-submit form
|
|
abandonment and validation friction are outside this denominator. No generic
|
|
click/field events are collected.
|
|
|
|
- `login`: one requested login through its service result.
|
|
- `signup`: signup acceptance, email delivery requests, then email confirmation.
|
|
- `verification`: confirmation/replacement delivery when there is no active signup.
|
|
|
|
Retries reuse the journey ID and get a new operation number. Signup success and
|
|
email delivery are intermediate states; confirmation success is the onboarding
|
|
terminal outcome. Login success is terminal. Failed operations remain recoverable.
|
|
Every operation has one completion, and every journey at most one terminal event.
|
|
Changing auth mode, returning from verification to login, changing a confirmation
|
|
token, or disposing the app ends the open attempt explicitly. A new operation
|
|
after a terminal outcome starts a new journey.
|
|
|
|
The inactivity window is **30 minutes after the last settled nonterminal service
|
|
result**, reset by a new request. It is suspended while a service operation is
|
|
pending: missing completion is not assumed to be abandonment. An explicit exit
|
|
can end an attempt while work is pending; it does not cancel backend work. Late
|
|
results cannot reopen that attempt or emit success after its abandonment.
|
|
|
|
IDs are random, scoped to this app instance and never derived from identity or
|
|
link tokens. Same-instance signup → demo link is correlated. A real email opened
|
|
in another tab/device or after reload starts a standalone verification journey.
|
|
Cross-device signup conversion needs a future backend-issued, non-sensitive
|
|
correlation contract; client code must never use email or the token as that ID.
|
|
Tab termination is not reliably observable. Missing exits remain **unknown**,
|
|
not inferred abandonment. Background timer throttling can delay idle detection.
|
|
|
|
## Event schema (version 1)
|
|
|
|
| Event | Emission condition | Product decision supported |
|
|
| --- | --- | --- |
|
|
| `auth_journey_started` | First valid request for a new attempt | Submitted-attempt denominator |
|
|
| `auth_{step}_requested` | A domain service operation starts | Locate the last meaningful step and retries |
|
|
| `auth_{step}_succeeded` | Service confirms a non-simulated result | Step conversion and outcome duration |
|
|
| `auth_{step}_simulated` | Dummy result, or terminal outcome of a mixed simulated journey | Validate the preview funnel without reporting business conversion |
|
|
| `auth_{step}_failed` | Service operation fails | Failure rates by safe error category and recovery |
|
|
| `auth_journey_abandoned` | Explicit exit or settled inactivity timeout | Diagnose known abandonment/drop-off |
|
|
|
|
`step` is `login`, `signup`, `email_delivery`, or `email_confirmation`.
|
|
Properties: schema_version, event_id, journey_id, journey_kind, sequence,
|
|
operation (per-journey counter when applicable), occurred_at (UTC), elapsed_ms
|
|
(monotonic journey duration), terminal, simulated, and optional typed failure/exit.
|
|
Every event in a known dummy journey is marked simulated. No email, password,
|
|
name, user ID, confirmation token, URL, raw exception, or form contents is captured.
|
|
|
|
## Metrics
|
|
|
|
Group by journey_kind and journey_id; deduplicate delivery by event_id, ordering
|
|
by sequence. Use **closed attempts** (terminal outcome or explicit abandonment)
|
|
for settled cohort rates; report open/unknown counts separately. For a fixed
|
|
reporting cohort, include starts in that interval and allow their defined
|
|
inactivity window to settle. Report pending operations separately even after it.
|
|
|
|
- Completion: distinct successful terminal journey IDs / closed journey IDs.
|
|
- Simulated completion: same calculation using simulated terminals, reported
|
|
separately. Never mix these into actual account/session conversion.
|
|
- Failure rate: distinct failed operations / requested operations; also report
|
|
journeys with any failure / started journeys.
|
|
- Recovery: closed journeys completing after a failed operation / closed
|
|
journeys with a failed operation.
|
|
- Known abandonment: abandoned journey IDs / closed journey IDs, by exit reason.
|
|
- Step drop-off: closed journeys reaching a step but not the next / closed
|
|
journeys reaching that step (signup acceptance → delivery → confirmation).
|
|
- Duration: terminal elapsed_ms; step latency from matching requested/result
|
|
operation timestamps. Report distributions separately for retries and failures.
|
|
|
|
Current service results are client evidence only. Future production adapters must
|
|
return success only after server confirmation; delivery API acceptance still
|
|
isn't proof that the email reached an inbox. That remains a documented proxy.
|
|
|
|
## Dummy service and PostHog replacement
|
|
|
|
`sxchnge_analytics` is a platform-independent shared package. `AnalyticsService`
|
|
accepts typed `AnalyticsEvent`; `DummyAnalyticsService` retains the latest 500
|
|
unique events in memory for tests/debugging. It exposes an immutable event list
|
|
and clear(), writes no logs/files, installs no SDK, and makes no network requests.
|
|
It is diagnostic storage, not a durable metrics backend; older events are evicted.
|
|
|
|
Inject the service through `SxchngeApp(analyticsService: service)`. Auth services
|
|
are decorated by `InstrumentedAuthServices`, so rebuilds, locale changes and
|
|
layout changes cannot generate events. Analytics exceptions are swallowed at the
|
|
recorder boundary and analytics futures are never awaited by auth operations.
|
|
Adapters must do lightweight capture/enqueue work; synchronous CPU blocking in an
|
|
adapter cannot be isolated by a Dart Future.
|
|
|
|
A future PostHog adapter should implement capture using event.name and the
|
|
allowlisted event.properties. Official API reference:
|
|
https://pub.dev/documentation/posthog_flutter/latest/posthog_flutter/Posthog-class.html
|
|
|
|
Preserve event_id and sequence for deduplication/reconstruction. Configure consent,
|
|
retention, region and anonymous identity before enabling collection. Do not enable
|
|
autocapture, session replay, automatic URL capture or user identification as a
|
|
side effect of this adapter: confirmation URLs and authentication forms contain
|
|
sensitive data. No PostHog key or network integration is included in this task.
|
|
Retries/buffering in a future adapter must preserve the original event_id and
|
|
occurred_at, remain bounded, and never delay authentication.
|
|
|
|
## UX review and verification
|
|
|
|
- Purpose/primary action: explicit demo outcomes and primary confirmation retry.
|
|
- Context/recovery: confirmed recipient separate from editor, focus restored by
|
|
Change email, no sent claim while editing; service failure retains input.
|
|
- States: initial/loading/sent/verified/expired/invalid/internal error remain
|
|
available. Restricted/offline backend behavior is outside this dummy service.
|
|
- Responsiveness: EN/RU and 200% text coverage retained; focused tests cover
|
|
keyboard submission, semantic heading/live region and mobile target labels/sizes.
|
|
- Measurement: tests cover correlated success, simulated outcomes, failures,
|
|
retries, duplicate completion/delivery, explicit exit, pending operations,
|
|
inactivity, and throwing/failing/non-completing analytics adapters.
|
|
- Privacy: payload tests exclude supplied form values and tokens; only typed
|
|
allowlisted properties are exposed. Dummy buffer is bounded and read-only.
|
|
- Remaining limits: no real PostHog delivery or cross-device correlation is
|
|
claimed. Automated semantics checks do not replace assistive-technology testing
|
|
on supported devices; a native VoiceOver/TalkBack audit remains a release check.
|
|
|
|
### Recorded validation
|
|
|
|
The automated suite passes 68 tests: 7 analytics, 20 auth, 3 theme, 3 theme
|
|
consumer, and 35 app tests. Formatting and analysis pass for the three affected
|
|
packages; the web build passes. Browser walkthrough at 390px covers internal-error
|
|
hierarchy, secondary email recovery, actual Tab/Enter submission, and the demo
|
|
sent screen. The heading is exposed as a live accessibility heading. Recipient
|
|
text uses a normal text node to remain exposed to assistive technology.
|
|
|
|
Delivered in [PR #13](https://git.sendico.io/tech/sxchnge/pulls/13), branch
|
|
`onboarding-12`. The final browser accessibility tree exposes the recipient text,
|
|
demo status, status heading and recovery actions. PR is open for review.
|