Skip to content

OAUTH-001C1I — Initialize App Check only after the Strava callback is clean #443

Description

@daliu

Officer impact: Members still see the same Strava connection screen. After the callback address is clean, the website prepares Firebase App Check before sending the made-up callback to the server; any preparation failure uses the existing plain retry result and sends no exchange.

Officer documentation: Update docs/officers/EVENTS_SHOP_MEMBERS.md and the matching current-source diagram/text alternative. Keep the procedure SOURCE ONLY, NOT LIVE until the website, configured Enterprise provider, Firebase Functions, and exact revisions are separately staged and verified.

Deployment evidence: None at creation. Closure must distinguish source, tests, merge, website publication, runmprc.com verification, Firebase deployment/readback, Enterprise provider configuration/token evidence, Strava/provider configuration, production data, and live behavior. This issue authorizes no provider console, key, secret, deployment, production-account, production-data, or live callback action.

Parent: #88. Slice: OAUTH-001C1I.

Problem

Exact main permanently suppresses App Check when a document starts on a capability callback. #335 now cleans both the native and React Router Strava callback locations, and #441 then sends the captured code and state to the exchange Function, but the same Firebase singleton still has no App Check provider. A hard reload would lose the memory-only callback values or require unsafe storage. Native callable enforcement cannot safely follow until this same-document clean handoff exists.

Atomic outcome

Add one complete client-side source/test handoff:

  • Preserve initial capability suppression so no Enterprise provider or token work starts while code, state, query, fragment, or Router state remains current.
  • Recognize that this document began on the normalized Strava callback, including approved encoded-segment, case, and trailing-slash forms.
  • After OAUTH-001C1G — Scrub Strava callback capabilities from current browser history before use #335 proves the current native and Router paths match and both locations have empty query/fragment plus null Router state, call one narrow idempotent FirebaseResources method.
  • Recheck the native path is still the same clean Strava callback, initialize the existing Enterprise App Check provider at most once, and await one token-ready result without accepting, returning, inspecting, logging, or storing code, state, token, or provider detail.
  • Recheck cleanup and current account/service/Firebase/app attempt after readiness, then make the existing exact {code,state} exchange once.
  • Missing public key, dirty/wrong path, Enterprise construction/initialization/token failure, hostile sync/async rejection, stale identity/service/app, unmount, reinjection, or obsolete completion uses the existing fixed callback failure and makes zero exchange.

Invariants and transitions

  • Initial capability document: suppressed -> native and Router clean -> readiness pending -> ready -> one existing exchange.
  • Any cleanup/readiness/lifecycle failure: suppressed or pending -> fixed stop; no exchange and no provider detail exposure.
  • Local and test runtimes keep App Check disabled and Firebase emulator isolation unchanged.
  • Ordinary production pages keep current eager Enterprise initialization unchanged.
  • The delayed method is Strava-callback-specific. Auth, registration, and shop capability pages cannot invoke it as a generic telemetry restart.
  • App Check initialization is not reversible. A later same-document reinjection is still scrubbed/discarded under OAUTH-001C1G — Scrub Strava callback capabilities from current browser history before use #335; this child rechecks around readiness but does not claim to erase earlier browser, hosting, provider, or network copies.
  • State creates no membership, discount, payment, member, or admin authority.
  • No schema, Rule, Function, package, lockfile, workflow, environment, provider configuration, migration, or backfill change.

Required proof

  • RED: old source begins the mocked exchange after successful cleanup while Enterprise initialization and token readiness remain absent.
  • Ordering: plain, percent-encoded segment, case-changed, and trailing-slash Strava callbacks suppress initial startup; every observed readiness and exchange boundary sees clean native and Router locations.
  • Positive: one clean current callback initializes Enterprise once, awaits one token-ready result, then exchanges the original made-up code/state once.
  • Negative: dirty or wrong native path, non-null Router state, missing key, constructor/init/token failure, sync/async hostile rejection, and wrong initial capability type stop with the fixed alert, no exchange, no canary, and no raw value inspection.
  • Idempotency: StrictMode, rerender, duplicate completion, and concurrent readiness calls produce at most one Enterprise initialization, one token request, and one exchange.
  • Lifecycle: UID, service, Firebase resources/app change, unmount, or route reinjection while readiness is pending makes completion inert and prevents exchange.
  • Compatibility: signed-out/loading/not-ready, provider error, missing code/state, cleanup failure/wait, later same-route callback, exchange rejection/success, replace navigation, ordinary production startup, Auth/registration/shop suppression, local/test emulator isolation, and SPA callback tests remain green.
  • Synthetic mocks only. No real key, App Check token, callback, Strava account/request, Firebase project, member record, or production data.
  • Focused FirebaseResources and Account tests, full frontend, SPA callback, lint, diagnostic build, unaffected Functions/Rules/emulator/workflow/artifact checks, and exact diff review pass.

Expected paths

  • src/services/monitoring/capabilityCallback.ts
  • src/services/firebase/FirebaseResources.ts
  • src/services/firebase/FirebaseResources.test.ts
  • src/pages/account/StravaCallback.tsx
  • named additive tests/support in src/pages/account/Account.test.tsx
  • narrow current-source truth in SYSTEM_DESIGN.md, SECURITY.md, and docs/officers/EVENTS_SHOP_MEMBERS.md
  • lint baseline only if the exact checker proves a mechanical line/fingerprint consequence

Dependencies and coordination

#99/#126, #159, #335, and #441 are merged on exact main a6b40d5e6042d6b5a26fe56a01165c1b4894029f. No open PR, assignee, active exact-path claim, or ready P0 issue was found immediately before publication. #113, #133, #136, Enterprise provider/key/domain configuration, protected release, and live proof remain owner/operations gates but do not block this mocked source bridge.

Explicitly out of scope

Do not add native runWith({enforceAppCheck:true}), change ENFORCE_APP_CHECK, inventory/migrate other callables, configure Enterprise, add a site key/debug token, change Functions/Rules/packages/workflows/environment, deploy Firebase or the website, contact Strava, inspect production data, use a real callback, or claim OAUTH-001C/#88 complete. Native enforcement and missing/invalid/valid runtime-token adoption remain the separate ABUSE-001A2 slice after this handoff and its staged provider gates.

Claim protocol

Creation is not a claim. Before editing: re-read live claims and open PRs; create and push a unique branch from exact released main; assign this child; post CLAIMED by <canonical agent> at <UTC>; branch <branch>; exact base <SHA>; move status:ready to status:in-progress; and cross-link #88. Earliest valid timestamp wins.

Metadata

Metadata

Assignees

Labels

area:authAuthentication and authorizationarea:firebaseFirebase services and dataarea:privacyPersonal data, consent, minimization, retention, and privacy operationsarea:stravaStrava integrationarea:webWeb application and hostingpriority:P0Launch blocker or urgent security risksize:MMedium multi-file issuetype:reliabilityReliability and recoverytype:securitySecurity or privacy boundarytype:testingTest infrastructure and quality gates

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions