OAUTH-001C1H: add one-use Strava OAuth state#442
Conversation
✅ Deploy Preview for luminous-fox-7c393f ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
SELF-REVIEWED by /root at exact head 21b6755 against exact base 89a0374. No actionable findings remain. I reviewed the complete remote 11-file diff for App Check/Auth ordering, request and stored-record bounds, UID/auth_time binding, digest-only storage, expiry on transaction retry, begin/exchange and concurrent-consumer races, full public state-denial uniformity, provider/write ordering, replay, no restoration, redacted failures/logs, browser single-flight and stale-context behavior, callback cleanup, Rules coverage, migration impact, source-vs-live wording, and officer continuity. Hosted CI run 29904118112 passed all five jobs at the exact head: frontend lint/test/SPA/workflow/build, Functions lint/test, Firestore Rules emulator, commerce concurrency emulator, and artifact scrubber. Netlify produced a preview only; its Header rules and Pages changed subchecks were neutral/skipped because those surfaces did not change, while Redirect rules and the preview passed. No production website, runmprc.com revision, Firebase deployment/readback, Strava/provider configuration, production data, or live OAuth behavior was verified. Review-required branch protection has no independent human approval, by design of the requested self-review workflow. The source is mergeable only through the authorized maintainer bypass after this exact-head evidence. |
Outcome
Strava connection now starts with a server-issued, ten-minute, one-use challenge that is bound to the signed-in UID and Firebase Auth session marker. The server stores only a SHA-256 digest and atomically consumes one valid challenge before credentials, provider access, or connection writes.
Officer impact: Members still start Strava from My Account, but a failed, expired, mismatched, or repeated callback now follows one plain retry path and cannot reach Strava or change the connection.
Officer documentation:
docs/officers/EVENTS_SHOP_MEMBERS.md; matching current-source design and risk records inSYSTEM_DESIGN.mdandSECURITY.md.Deployment evidence: Source and synthetic local tests only. Website publication: not performed.
runmprc.comrevision verification: not performed. Firebase deployment/readback: not performed. Strava/provider configuration or contact: not performed. Production data and live behavior: not inspected or verified.Security and transitions
stravaBeginAuthorization: App Check -> Auth and positive safe-integerauth_time-> 32 random bytes -> one digest-only fixed secret record.stravaExchangeCode: App Check -> Auth -> exact code/state parsing -> Firestore transaction -> one delete -> existing exchange path.permission-deniedpublic result.Verification
89a03741d94d1509ab4b49a203f9b0244a7b3797: the old exchange reached mocked Strava without a server challenge; 1 intended failure, 486 skipped.demo-rules-testwith Java 17.demo-pay002b2-test.git diff --check: passed; no package, lockfile, Rules, workflow, sitemap, provider, or production-data change.Review and residual risk
Independent frontend/officer and backend security reviews are clean after correcting stale current-truth documentation, retry-time expiry, full state-error uniformity, and credential-failure consumption proof. The initial capability callback still suppresses browser App Check startup, so fail-closed callback compatibility remains separate work under #88. Firestore race proof uses an optimistic transaction mock rather than deployed Firebase. Native App Check enforcement, scope/account policy, refresh concurrency, reconciliation, revoke/audit, IAM/encryption, provider setup, deployment, and live proof remain out of scope.
Closes #441.
Parent #88 remains open.