# Epic 006 — Implementation stories

These stories are deliberately limited to repository-supported findings. The epic is
specification-only: all tasks begin unchecked and no runtime change is implied by their presence.
Task IDs use `SEC-TASK-NNN`; acceptance scenarios are stable and must not be renumbered after
implementation evidence is added.

## Ordered backlog

| Task                          | Story                                                                       | Outcome                                                                        | Depends on | Priority | Status      |
| ----------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ---------- | -------- | ----------- |
| [SEC-TASK-001](#sec-task-001) | [SEC-AUTH-001](#sec-auth-001-require-https-at-hosted-credential-boundaries) | Reject insecure hosted browser/payment endpoints and verify public transport   | None       | P1       | In progress |
| [SEC-TASK-002](#sec-task-002) | [SEC-AUTH-002](#sec-auth-002-bound-authentication-abuse-controls)           | Bound auth-attempt state and verify abuse controls against deployment topology | None       | P2       | In progress |

## SEC-AUTH-001 — Require HTTPS at hosted credential boundaries

### SEC-TASK-001

- **Status:** In progress
- **Priority:** P1. The potential impact of password/session or provider-secret disclosure is high;
  actual deployed HTTP exposure has not been established.
- **Finding:** [SEC-F-001](README.md#confirmed-findings)
- **Dependencies:** None.
- **Context and evidence:** `login`, password change, and reset serialize credentials into JSON
  request bodies through the shared API client. This is safe in transit only when the configured
  public endpoint is HTTPS. The backend origin schema accepts both `http:` and `https:` for hosted
  `PUBLIC_BROWSER_ORIGIN`; hosted validation explicitly requires HTTPS for `APP_PUBLIC_URL` but does
  not apply that condition to the browser origin or CORS origins. `SURFBOARD_API_URL` is validated
  as a URL without an HTTPS scheme constraint and the provider transport sends configured
  authentication headers to it. The frontend runtime/build API base is not protocol-validated. The
  development guide prescribes HTTPS in the hosted example, but Render/edge redirect behavior and
  actual deployed values are not present in the repository.
- **Goal:** Make HTTPS an enforced hosted configuration invariant for browser/API traffic and all
  credential-bearing payment-provider calls, then verify the actual staging and production edge.
- **Scope:** Hosted config validation; frontend build/runtime API-base validation where an absolute
  URL is configured; tests and development/deployment documentation; redacted staging/production
  verification. Preserve same-origin relative API paths and local/test HTTP workflows. Keep the
  existing cookie-session architecture.
- **Non-goals:** Client-side password encryption, replacement of cookie sessions, changes to
  credentials in JSON bodies, implementation of a new proxy/CDN, or any Live Surfboard call.
- **Implementation guidance:** Validate URL protocols explicitly rather than relying on generic URL
  parsing. Do not silently rewrite a configured `http:` endpoint to `https:`. Keep server-side
  Surfboard secrets out of build-time frontend values. A top-level HTTP-to-HTTPS redirect is not
  proof that a credential-bearing HTTP POST was protected; confirm the browser/API cannot submit
  credentials over HTTP, and verify HTTP Strict Transport Security at the browser-facing HTTPS
  origin according to the deployment's domain boundary.
- **Acceptance scenarios:**

  ### SEC-AUTH-001-S01 — Hosted origins reject HTTP

  Given `DEPLOYMENT_ENV` is `staging` or `production`, when browser, CORS, or application origins
  are configured with `http:`, startup fails with a safe field-specific configuration error. Local
  and test configurations may continue to use their documented HTTP origins.

  - **Evidence:** Focused `backend/src/config.test.ts` cases for hosted HTTP rejection and
    local/test HTTP acceptance; no secret values in assertion output.

  ### SEC-AUTH-001-S02 — Payment credentials never target HTTP

  Given Surfboard checkout is configured in any environment, when `SURFBOARD_API_URL` uses `http:`,
  configuration is rejected before the adapter can send `API-KEY`, `API-SECRET`, or `MERCHANT-ID`.

  - **Evidence:** Focused config/composition tests with an injected transport proving the HTTP case
    makes no request; existing adapter tests continue to prove redirects fail closed.

  ### SEC-AUTH-001-S03 — Hosted browser authentication stays on HTTPS

  Given the staging or production browser and API are deployed, when a reviewer inspects the browser
  origin, API origin, auth request destinations, and HTTP behavior, all credential-bearing requests
  use HTTPS, the HTTPS origin advertises the approved HTTP Strict Transport Security policy, and the
  hosted session cookie remains `Secure`, `HttpOnly`, and `SameSite=Lax`. An HTTP POST must not be
  forwarded or replayed with its credential body as a purported transport safeguard.

  - **Evidence:** Redacted manual verification for both hosted environments, recording public
    origins, request schemes, redirect/rejection result, response security headers, and cookie
    attributes without credentials or session values. This evidence cannot be produced by local
    tests and is required before marking this scenario complete.

- **Verification:** Run the focused backend configuration, payment transport, and frontend API
  client suites; run `pnpm quality`. Repeat the external browser/edge check after deployment.
- **Documentation/contracts:** Update the configuration and staging deployment guide. Update OpenAPI
  only if the externally visible API contract or documented headers change.
- **Deployment/operations:** Correct existing hosted values before deploying stricter startup
  validation. Verify Render's proxy/TLS behavior instead of weakening Secure cookies or trusting
  unknown proxy ranges. Record staging and production evidence separately.
- **Repository evidence:** `backend/src/config.test.ts` covers both hosted classifications,
  local/test HTTP acceptance, safe field-specific rejection, and HTTPS Surfboard configuration.
  `frontend/src/app/api/api-client.test.ts` covers HTTP rejection, HTTPS acceptance, loopback HTTP,
  and same-origin paths. Hosted browser/TLS/HTTP Strict Transport Security/cookie evidence (S03) is
  still required.

## SEC-AUTH-002 — Bound authentication abuse controls

### SEC-TASK-002

- **Status:** In progress
- **Priority:** P2. The stale map is a confirmed unbounded-memory path on public authentication
  routes; cross-instance bypass and production request volume are not established.
- **Finding:** [SEC-F-002](README.md#confirmed-findings)
- **Dependencies:** None.
- **Context and evidence:** `registerAuthHttp` constructs one in-process `accountAttempts` map
  shared by login, recovery, reset, and password-change routes. `consumeAccountAttempt` adds hashed
  account keys and increments their counts, but never deletes expired entries. Per-IP
  `@fastify/rate-limit` and the account map are process-local; ADR 002 already records that counters
  reset at restart and are not coordinated across instances.
- **Goal:** Make authentication-abuse state finite over time without weakening active account
  windows, disclosing raw login identifiers, or turning an internal storage failure into a
  success-shaped response.
- **Scope:** Expiration/cleanup and safe bounded-state behavior for account throttles, deterministic
  unit and route regression tests, and a verified description of the hosted instance topology and
  effective limits. Preserve generic invalid-credential and recovery responses, existing defaults,
  and per-IP protections.
- **Non-goals:** Account lockout that enables targeted denial of service, CAPTCHA, new third-party
  rate-limit infrastructure without deployment evidence, logging raw identifiers, or destructive
  load testing.
- **Implementation guidance:** Use an injectable clock or equivalent deterministic boundary for
  expiry tests. A cleanup or capacity policy must not silently evict a live account window and
  permit unlimited verification. If deployment supports multiple instances, ensure the effective
  abuse-control boundary is understood and enforced at the appropriate shared/edge layer or
  explicitly constrain the supported topology; do not claim a process-local map is global.
- **Acceptance scenarios:**

  ### SEC-AUTH-002-S01 — Expired account keys are reclaimed

  Given attempts have populated account throttle keys, when their configured windows expire and
  subsequent requests are processed, expired entries are reclaimed and the store does not retain
  account keys indefinitely.

  - **Evidence:** Deterministic unit tests with a controlled clock, including many distinct account
    keys over multiple windows; no load test or real account data.

  ### SEC-AUTH-002-S02 — Active limits and privacy behavior are preserved

  Given multiple attempts target one account or source IP, when the configured threshold is reached,
  requests still receive the established `429`/`Retry-After` behavior, valid users receive no
  account-enumeration signal, and raw email addresses/passwords never become throttle keys or log
  fields.

  - **Evidence:** Backend HTTP tests for per-IP/per-account throttling, unknown-account parity, and
    structured-log redaction; existing authentication regression suites remain green.

  ### SEC-AUTH-002-S03 — Hosted limits match the supported process topology

  Given the deployed backend instance count and edge configuration are inspected, when the effective
  per-IP and per-account limits are documented, their scope and reset behavior match the actual
  topology and do not overstate cross-instance protection.

  - **Evidence:** Redacted staging/production deployment evidence or an explicit supported
    single-instance constraint, linked from the task status. Local unit tests alone do not satisfy
    this scenario.

- **Verification:** Run focused `backend/src/auth-http-support.test.ts` (or its resulting focused
  throttle tests), auth route suites, and `pnpm quality`; run no destructive load test.
- **Documentation/contracts:** Update ADR 002 or the development guide to reflect the implemented
  storage/cleanup and actual topology. Update OpenAPI only if the public throttling contract
  changes.
- **Deployment/operations:** Confirm proxy-derived client IPs are trusted only through verified
  `TRUSTED_PROXIES` configuration. Review instance scaling and edge rate limits with the deployment
  owner.
- **Repository evidence:** `backend/src/auth-http-support.test.ts` uses explicit timestamps to cover
  expiry boundaries, repeated distinct keys, preserved active windows, retry calculations, hashed
  account keys, and absence of submitted credentials from throttle keys. Existing recovery, session,
  and account-settings HTTP suites cover 429 and Retry-After behavior. Deployed topology evidence
  (S03) remains outstanding.

## Explicitly deferred from this backlog

- **Role/tenant authorization:** Current behavior is the documented single-store trusted-operator
  model, not a per-user ownership model. The product owner must decide whether less-trusted user
  classes or multiple businesses enter scope before a role/membership design is specified.
- **Retention, export, correction, and deletion:** The repository shows durable order/history data
  but does not define the business purpose, statutory retention, controller, processor locations,
  backup lifecycle, or response process. The business/data controller must decide those first; do
  not invent deletion periods or erase financial/order facts based only on code inspection.
- **Hosted TLS, response headers, data residency, and backups:** These depend on provider-side
  configuration absent from the repository. Verify them operationally; do not report them as missing
  in production without observing the deployed services.
- **Surfboard webhook processing/replay:** The current authenticated ingress verifies signatures and
  acknowledges without mutating order state. Replay/idempotency requirements become an
  implementation story only when the provider event is wired to reconciliation, following ADR 004
  and the existing payment-safety contract.
