Raxx · internal docs

internal · gated

ADR 0051 — Fidelity auth flow: 3-legged OAuth 2.0 with PKCE; no credential hand-off to Raxx

Status: Proposed — contingent on WIX partnership (ADR 0050)
Date: 2026-05-05
Deciders: software-architect, operator
Context doc: fidelity-broker-integration.md §6
Related ADRs: 0002, 0009, 0014, 0050


Context

Raxx must authenticate on behalf of each customer against the Fidelity WIX API. Several auth flows are available for broker API integrations:

  1. 3-legged OAuth 2.0 with PKCE — customer authorizes Raxx at Fidelity's consent screen; Raxx receives tokens scoped to the customer's account.
  2. Customer credential passthrough — customer provides their Fidelity username + password to Raxx; Raxx authenticates on their behalf.
  3. SAML federation — enterprise SSO pattern; not applicable for a retail SaaS product.
  4. Static API key issued by Fidelity to Raxx — Raxx holds a single key that grants access to all enrolled customers' accounts.

This ADR records the chosen flow and the rationale.


Decision

3-legged OAuth 2.0 with PKCE (authorization code grant).

The customer authorizes Raxx at Fidelity's consent screen (browser redirect). Raxx receives an access token and (if issued) refresh token scoped to the customer's account and requested scopes. Raxx never sees the customer's Fidelity password.

This is the same pattern used for Alpaca live-handoff (ADR 0014), normalized under the forthcoming BrokerAdapter abstraction (ADR 0052).

Token storage at rest:
Access token and refresh token are stored encrypted using envelope encryption (KMS-backed wrapping key, same pattern as alpaca_live_connections). No plaintext token column is permitted in the schema. This is an explicit, narrowly-scoped exception to invariant I1 (no stored credentials), identical to the exception already accepted in ADR 0009 / ADR 0014.

The exception applies only to fidelity_live_connections rows for Pro+ users who have explicitly enrolled live-handoff. Every other user has no Fidelity credentials stored.

Raxx's platform credentials (WIX OAuth client_id + client_secret):
These live in Infisical at /raxx/v1/{env}/fidelity/. Never in code. Rotatable without redeploy. Rotated via Velvet (FidelityOAuthClientAdapter, pre-staged-mint pattern identical to PostmarkSenderTokenAdapter).


Consequences

Positive

Negative


Alternatives considered

Customer credential passthrough (Fidelity username + password stored by Raxx)

Rejected. This is the most obvious violation of invariant I1 — "the system must not have the ability to replay a credential." Storing or transmitting Fidelity credentials would also violate Fidelity's ToS and expose Raxx to catastrophic regulatory and liability risk. This option is not revisitable under any circumstances.

Static API key (Raxx holds one key, all customers' accounts)

Rejected. This model puts all enrolled customers' accounts behind a single credential. A key compromise would expose every customer simultaneously. Also unlikely to be offered by WIX at the retail-customer granularity level required.

SAML federation

Not applicable. SAML is an enterprise SSO pattern for workforce identity. Retail customers authenticating from consumer devices do not have a SAML IdP Fidelity can federate against.

Short-lived access-only (no refresh token storage)

Considered. If WIX issues non-refreshable short-lived access tokens (e.g., 1-hour), Raxx could operate with no stored refresh token — customer re-authorizes each session. This is a cleaner posture vis-à-vis invariant I1.

Deferred. WIX token lifetime and refresh availability are unknown until the partnership agreement is in place. If WIX tokens are short-lived and non-refreshable, the storage model simplifies (no refresh token row). The fidelity_live_connections schema accommodates both cases: refresh_token_ciphertext is nullable.


Revisit when