Connect sign-up to Sendico Facts intake (#46)
This commit is contained in:
1 parent
30f0455122
commit
a584c8189a
40 files changed
+1616
-35
No files matched your search
@@ -61,7 +61,7 @@ envelope. Credentials, codes and activation tokens are sent in POST bodies.
|
||||
|
||||
| Route | Request | Successful result |
|
||||
| --- | --- | --- |
|
||||
| POST `/signup` | `name`, `email`, `password`, `locale`, `idempotencyKey` | 202 with `userId`, after activation email queue acceptance |
|
||||
| POST `/signup` | `name`, `email`, `password`, `locale`, `idempotencyKey` | 202 with `userId`, after configured Facts intake and activation email queue acceptance |
|
||||
| POST `/verification/send` | `email`, `idempotencyKey` | 202; generic acceptance also applies to absent/already-active accounts |
|
||||
| POST `/verification/confirm` | `token` | 202 with `verified: true` after activation commits |
|
||||
| POST `/login` | `login: {login, password}`, `clientId`, `deviceId` | 202 with `userId`, `pendingToken: {token, expiration}`, `challengeId`, `channel`, masked `target`, `expiresAt`, `resendAt` |
|
||||
@@ -101,6 +101,14 @@ retries can create replacement messages; they are not distributed exactly-once
|
||||
operations. Tokens are stored before the RPC, so a process crash between those
|
||||
steps requires user recovery; there is no product-side delivery outbox.
|
||||
|
||||
When `api.sendico` is enabled, sign-up and activation resend first require a
|
||||
durable Facts intake receipt. Activation checks that receipt in its transaction.
|
||||
Initial account/token creation uses the same server-secret-derived activation
|
||||
key as a same-key retry, so an intake outage does not force an extra cooldown for
|
||||
an unsent initial token. Existing pending customers created before this change
|
||||
retain the prior cooldown behavior. See [Facts integration](signup-facts.md) for
|
||||
uncertain registration recovery and the `signup_attention_required` error.
|
||||
|
||||
Code verification, one-use proof consumption and session creation now commit in
|
||||
one transaction. A session-write failure leaves the code retryable; a lost HTTP
|
||||
response after commit requires restarting password login. The adapter
|
||||
|
||||
@@ -828,3 +828,55 @@ existing mount requires reauthentication at its new cookie path. Notification
|
||||
activation URLs remain configured frontend URLs. No live deployment, optional
|
||||
security scans or merge were performed. The unrelated local notifications sender
|
||||
configuration remains uncommitted. See [API version installation](api-versions.md).
|
||||
|
||||
## Sign-up Facts intake — task #46
|
||||
|
||||
[Task #46](https://git.sendico.io/tech/sxchnge/issues/46), branch
|
||||
`identity-facts-46`, connects the product sign-up service to Sendico's public
|
||||
individual registry and immutable Facts intake through optional `api.sendico`
|
||||
configuration. Owner: `tech`; project: SXCHNGE; labels: backend and frontend.
|
||||
|
||||
The adapter sends full name, normalized email and the local customer reference.
|
||||
The BFF persists a customer-owned subject binding, immutable facts snapshot and
|
||||
confirmed receipt before activation email or sign-up acceptance. Activation and
|
||||
resend enforce the handoff. The shared database source can require majority
|
||||
writes and primary majority reads without bypassing its connection/transaction
|
||||
lifecycle. Initial account/token creation remains atomic and uses a secret-derived
|
||||
activation key, allowing same-key recovery after an intake outage without an
|
||||
extra cooldown for an unsent token. Archived customers cannot resume sign-up.
|
||||
|
||||
Backend and frontend completion reviews covered the full task contribution,
|
||||
including the public contracts, customer ownership, stable replay namespace,
|
||||
concurrent claims, error classification, credential handling, logging, metrics and
|
||||
DTO/mapper/service/model/provider separation. UX review preserved input, loading
|
||||
suppression and confirmation-based success. Typed support guidance reaches both
|
||||
sign-up and verification resend/confirmation through the shared auth package;
|
||||
EN/RU feedback preserves live-region semantics and existing responsive controls.
|
||||
The new localized widget fixture initially double-disposed the app-owned locale
|
||||
provider; the fixture ownership was corrected. Focused integration tests also
|
||||
exposed the initial-token cooldown issue, which was fixed in the creation flow.
|
||||
|
||||
Individual registration is not idempotent upstream. A durable claim prevents
|
||||
speculative repeat registration after an ambiguous result; reconciliation uses
|
||||
the saved request reference and authoritative Sendico records. Intake replay is
|
||||
safe after receipt loss and retains original assertions. Actual provider IDs,
|
||||
the endpoint and a same-account Vault-rendered access token must be provisioned
|
||||
before enabling this integration. Tests use simulated HTTP and disposable MongoDB;
|
||||
no live provider resources, live email, deployment, merge or optional security
|
||||
scan is part of this delivery. The unrelated notifications runtime configuration
|
||||
is excluded. Configuration, rollout, telemetry and recovery details are in
|
||||
[Sign-up Facts intake](signup-facts.md).
|
||||
|
||||
Final validation passed: `GOFLAGS=-p=2 make test` (all backend race tests and
|
||||
`make test-frontend` suites), `make lint-backend`,
|
||||
`dart format --output=none --set-exit-if-changed` for all 11 changed Dart files,
|
||||
and `flutter analyze` in `frontend/app` and `frontend/packages/sxchnge_auth`.
|
||||
Focused Go race suites and Flutter backend/provider/widget tests also passed;
|
||||
the diagnostic public API run
|
||||
`go -C backend/product test -race -v ./auth/http/v1/public -count=1 -timeout=90s`
|
||||
passed. The initial unrestricted `make test` failed when Testcontainers could
|
||||
not start its reaper before the Mongo fixture deadline. Lower Go package
|
||||
parallelism resolved that infrastructure contention. The next full run exposed
|
||||
the new locale fixture disposal error noted above; the corrected final run
|
||||
passed, including EN/RU support states and verification recovery. No checks were
|
||||
weakened or unrelated work changed.
|
||||
@@ -0,0 +1,176 @@
|
||||
# Sign-up Facts intake
|
||||
|
||||
Task [#46](https://git.sendico.io/tech/sxchnge/issues/46), branch
|
||||
`identity-facts-46`. Primary module: identity-facts; secondary modules: auth,
|
||||
customer creation, runtime, shared database source and Flutter error feedback.
|
||||
|
||||
## Behavior and boundaries
|
||||
|
||||
With `api.sendico` configured, the product BFF creates the local pending customer
|
||||
and activation token atomically, then registers an independent individual through
|
||||
Sendico's public HTTP API and submits the sign-up assertions. It persists the
|
||||
intake receipt before sending activation email or returning sign-up success.
|
||||
Activation resend runs the same handoff; activation checks the receipt in its
|
||||
local transaction, without external I/O. A failed receipt check rolls back token
|
||||
consumption and leaves the account pending. Existing active customers are not
|
||||
backfilled or blocked at login.
|
||||
|
||||
The adapter consumes these existing Sendico contracts:
|
||||
|
||||
- `POST /individuals/{organization_ref}`, empty body, HTTP 200 registration
|
||||
receipt containing `subject_ref` and `registered_at`.
|
||||
- `POST /identity_facts/{organization_ref}`, HTTP 200 immutable intake receipt
|
||||
containing `submission_ref`, `subject_ref` and `accepted_at`.
|
||||
|
||||
The configured API prefix precedes both paths. See Sendico
|
||||
[individual subjects](https://git.sendico.io/tech/sendico/src/branch/main/docs/api/individual-subjects.md)
|
||||
and [Facts intake](https://git.sendico.io/tech/sendico/src/branch/main/docs/api/identity-facts-intake.md).
|
||||
The only assertions sent are `full_name`, normalized `email` and
|
||||
`external_customer_ref` (the local customer ID). Passwords, password hashes,
|
||||
activation tokens, session credentials and device data never enter the adapter.
|
||||
The input name is retained as a full name, without guessing name parts. No facts
|
||||
are marked verified; acceptance does not establish identity, KYC completeness,
|
||||
verification, limits or payout eligibility. The existing KYC preview is unchanged.
|
||||
|
||||
Only the BFF owns Sendico credentials and organization scope. Local customer
|
||||
models have no organization, membership or platform permissions. HTTP DTOs and
|
||||
mappers stay in `providers/sendico`; the handoff operates on domain facts and
|
||||
receipts in `identity`. Flutter continues through its DTO, mapper, service,
|
||||
model and provider layers. It never calls Sendico or supplies subject references.
|
||||
|
||||
## Configuration and rollout
|
||||
|
||||
Add this block under `api` in the product service configuration, retaining
|
||||
`api.notifications`:
|
||||
|
||||
```yaml
|
||||
sendico:
|
||||
api_url: https://your-sendico-host/api/v1
|
||||
organization_ref: <provisioned-SXCHNGE-organization-object-id>
|
||||
source_account_ref: <provisioned-integration-account-object-id>
|
||||
token_file: /run/sxchnge/sendico-token
|
||||
```
|
||||
|
||||
Resolve the real IDs from the target Sendico deployment. The integration account
|
||||
must be a member of the SXCHNGE organization with the existing organizations
|
||||
`update` permission; reconciliation GET requests also require `read`. The token
|
||||
must be a full, unexpired Sendico access JWT whose `id` claim is that account.
|
||||
The adapter inspects claims only to protect the replay namespace; Sendico checks
|
||||
the signature and authorizes every call. No login, refresh or privileged account
|
||||
provisioning is performed by this adapter.
|
||||
|
||||
Keep the token in Vault at `services/bff/sendico`, field `access_token`. Render it
|
||||
with the deployment's Vault Agent into the configured private runtime file; give
|
||||
only the BFF policy read access to that KV path. Endpoint, organization and source
|
||||
IDs remain in service YAML. For the local stack, configure
|
||||
`infra/local/bff/runtime.yml`, render to the BFF's existing `/run/sxchnge` tmpfs,
|
||||
and extend its Vault Agent policy/template for this provisioned secret. These
|
||||
deployment values and secret provisioning are deliberately not guessed or
|
||||
enabled by the example. Do not place tokens in Git, browser builds or shell
|
||||
history. Refresh the file atomically before expiry using the same Sendico account;
|
||||
the adapter rereads it on every request. Startup rejects missing, expired or
|
||||
mismatched credentials and rejects Sendico configuration without notifications.
|
||||
|
||||
HTTPS is required except for loopback HTTP test/development endpoints. Redirects
|
||||
are rejected and each HTTP call has a four-second timeout. Non-200 responses,
|
||||
malformed/truncated receipts, invalid identifiers, wrong subjects and unsuccessful
|
||||
envelopes never count as acceptance. Upstream error bodies are not surfaced.
|
||||
|
||||
Omitting `api.sendico` keeps existing local-only behavior. Enabling it also gates
|
||||
activation for previously pending customers; use activation resend to submit
|
||||
their saved name/email before confirming their old link. There is no automatic
|
||||
bulk migration. Changing endpoint, organization or source account changes the
|
||||
adapter fingerprint and blocks existing bindings for explicit reconciliation.
|
||||
Token rotation for the same account retains that fingerprint. Plan such changes
|
||||
as migrations; never reset a binding just to bypass the check.
|
||||
|
||||
## Persistence, retries and recovery
|
||||
|
||||
The local `signup_facts` collection uses the customer ID as `_id`, so its unique
|
||||
primary key coordinates concurrent requests and separate BFF instances. It stores
|
||||
an immutable sign-up facts snapshot, adapter fingerprint, random registration
|
||||
request correlation, registration claim, subject and confirmed receipt. It uses
|
||||
the shared database repository and connection lifecycle with majority writes and
|
||||
primary majority reads, overriding weaker connection defaults. No external call
|
||||
runs in a database transaction. Snapshots and bindings contain personal data and
|
||||
must follow the customer data retention policy; no TTL silently removes replay
|
||||
state, and customer deletion does not delete the immutable Sendico submission.
|
||||
|
||||
Once a subject is stored, intake retries use
|
||||
`sxchnge:signup:<local-customer-id>` as `source_submission_ref`. Sendico's
|
||||
organization/authenticated-account/source-reference uniqueness returns the
|
||||
original receipt for that subject. Retries send the saved snapshot even if a
|
||||
later request changes name or email. Losing the HTTP receipt or a local receipt
|
||||
write is recoverable by replay. A saved receipt skips subsequent external calls.
|
||||
Retrying sign-up still requires the original password; active, archived or
|
||||
otherwise conflicting accounts cannot be resumed as a new customer.
|
||||
|
||||
**Individual registration is not idempotent.** The BFF commits a claim before
|
||||
dispatch and never expires or automatically retries that registration. It cannot
|
||||
prove that a timed-out request, malformed response or failed local binding write
|
||||
did not create a subject. Another request sees an in-progress claim as temporarily
|
||||
unavailable for 30 seconds; an older unresolved claim requires support. The
|
||||
registration caller immediately reports assistance on an uncertain failure. An
|
||||
accepted registration is saved with a short independent context even if the
|
||||
browser disconnects. A crash before dispatch can also require reconciliation.
|
||||
This trades automatic recovery for avoiding duplicate subjects under the current
|
||||
upstream contract; there is no distributed exactly-once guarantee.
|
||||
|
||||
For an unresolved registration, operators must stop requests for the affected
|
||||
customer and inspect the saved `registrationRequest` plus Sendico identity logs
|
||||
(`X-Request-ID`, operation `subject_register`) under the configured account and
|
||||
organization. A successful registration's server-issued `record_ref` identifies
|
||||
the candidate subject. Confirm it using the organization-scoped individual GET
|
||||
API before atomically setting the matching local binding's `subjectRef` and
|
||||
clearing `registering`; retain its snapshot, fingerprint and request reference.
|
||||
If a local binding write failed after receipt, BFF diagnostics also contain the
|
||||
subject reference. If authoritative records prove no subject was created, an
|
||||
operator may clear the claim after fixing the dependency. If neither result can
|
||||
be established, leave it blocked; never register a speculative replacement.
|
||||
Resume sign-up with the original credentials or activation resend after this
|
||||
repair. These are controlled operational repairs, not customer API endpoints.
|
||||
|
||||
HTTP 503 `signup_attention_required` maps to typed shared auth and verification
|
||||
state and localized EN/RU support guidance, including activation resend recovery.
|
||||
Other intake/receipt failures remain temporary service
|
||||
failures. Form input is retained, duplicate submissions stay disabled during
|
||||
loading, and neither failure navigates to an email-sent screen or creates a
|
||||
session. The existing support channel must be available to handle reconciliation.
|
||||
|
||||
## Measurement and UX review
|
||||
|
||||
The existing [auth journey](ux/auth-journeys.md) keeps its attempt, retry,
|
||||
abandonment and 30-minute settled inactivity rules. `auth_signup_succeeded` now
|
||||
requires server confirmation of configured intake plus activation queue acceptance;
|
||||
it remains an intermediate step and email confirmation remains the terminal
|
||||
onboarding outcome. Sign-up dependency failures and assistance outcomes use the
|
||||
existing allowlisted `unavailable` analytics category; verification assistance
|
||||
uses it too. Retries retain the journey
|
||||
and operation semantics. No names, emails or provider response bodies are added.
|
||||
|
||||
`sxchnge_signup_facts_total{outcome}` records `accepted`, `replayed`,
|
||||
`unavailable` and `attention_required` handoff attempts;
|
||||
`sxchnge_signup_facts_duration_seconds` measures their latency. Counts are
|
||||
attempts, not distinct users. Use unavailable/attention counts divided by total
|
||||
handoff attempts for operational failure rates; use the existing correlated auth
|
||||
journey for conversion, duration and abandonment. Structured handoff logs include
|
||||
local customer and random registration request references for support correlation,
|
||||
without asserted values, credentials or raw errors. IDs are never metric labels.
|
||||
The product team uses the distinction to separate retryable intake outages from
|
||||
registration reconciliation. The default client analytics sink remains in memory;
|
||||
production retention and cross-device conversion correlation remain deployment
|
||||
gaps, as documented by the existing journey contract.
|
||||
|
||||
Review covers loading suppression, input preservation, success only on confirmed
|
||||
handoff, temporary retry and assistance states, existing navigation, mobile layout,
|
||||
text scaling and live-region error feedback. Focused tests cover these states,
|
||||
the mapped backend error, non-inflated client success, immutable retry payloads,
|
||||
credential rotation, timeout/cancellation/redirect behavior, receipt validation,
|
||||
privacy-safe errors, concurrent durable claims and activation rollback. The tests
|
||||
use disposable local MongoDB and a simulated Sendico HTTP server; they do not
|
||||
create live subjects or send email. Live integration and support operations must
|
||||
be verified against provisioned deployment credentials before enabling the block.
|
||||
The EN/RU error state is exercised at a 390px viewport with 1.5x text scaling;
|
||||
existing keyboard regression tests and a code review of the live-region semantics
|
||||
cover the unchanged controls. No manual browser or screen-reader walkthrough
|
||||
against a live Sendico deployment was performed.
|
||||
Reference in new issue
Block a user