Skip to content

OAUTH-001A1H — Refuse stale Strava refresh persistence - #607

Merged
daliu merged 1 commit into
mainfrom
codex/issue-606-strava-refresh-cas
Aug 2, 2026
Merged

daliu merged 1 commit into
mainfrom
codex/issue-606-strava-refresh-cas

Conversation

@daliu

@daliu daliu commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Outcome

A Strava refresh response can now update the stored secret only when a Firestore transaction rereads the same exact access-token, refresh-token, and expiry tuple.

Closes #606
Atomic child of #88.

Scope

  • Issue: OAUTH-001A1H — Refuse stale Strava refresh persistence #606
  • What changed: functions/strava.js rereads and validates the exact stored token tuple in one read-before-write transaction; it merge-writes the admitted provider response only after an exact match. Changed, missing, malformed, conflict-retried-to-changed, reference, read, set, commit, and acknowledgement-failure paths stop with the existing fixed private refresh result and make no downstream bearer call.
  • What changed in proof: functions/strava.test.js adds transaction-set mock fidelity and synthetic stale rotation, individual-field change, deletion, malformed replacement, conflict retry, unchanged success, write failure, and acknowledgement-loss coverage. SECURITY.md records the source-only RISK-024 boundary and residuals.
  • What did not change: provider-call serialization or leasing, identical-tuple replacement detection, response-body limits, client disconnect behavior, OAuth scope/account policy, schema, Rules, provider settings, deployment, production data, or live behavior.
  • Migration impact: None. The existing three-field stored tuple is the compare-and-set fence.

The design follows the provider requirement to persist and use the latest returned refresh token: https://developers.strava.com/docs/authentication/
The transaction uses the documented Firestore retry model, in which a concurrent edit reruns the read/write function and successful writes apply atomically: https://firebase.google.com/docs/firestore/manage-data/transactions

Officer handoff

  • Officer impact: After a future protected Functions release, a slow Strava refresh response cannot persist after the stored access-token, refresh-token, or expiry tuple changes, disappears, or becomes malformed. There is no current page change or officer action.
  • Officer documentation: None — this is a server-only secret-persistence boundary. Existing SOURCE ONLY / NOT LIVE Strava failure guidance remains accurate.
  • Approving role: Repository maintainer plus security reviewer; a platform owner is still required for any future protected Firebase release.
  • Preview or redacted screenshot: None — no page or visible workflow changed.
  • Screenshot checked at: Not relevant.
  • Plain-language undo plan: Revert exact commit 49e81b5 in a reviewed pull request. If a future separately authorized Firebase release includes this Function, read back and roll back that exact Function revision through the protected operator path; no such release is part of this PR.
  • Deployment evidence: Source and synthetic tests only. No website publication, runmprc.com verification, Netlify production release, Firebase deployment, Strava configuration or call, production-data action, or live behavior occurred.

Proof by surface

  • Source changed: exact head 49e81b5; parent a62c147; tree 6b3a0db31e87ca7e247c92835f4ccd1a575e0bbf; patch SHA-256 bc2c962c56329bef7aa5664542c71b34c05d19a03f439c769910a25c26dcfef5.
  • Tests passed: trustworthy RED on the exact base was 1 failed / 7,567 total with test-only diff SHA-256 35821aa35db663cca52871ad6476c41e33055d7b73f9d285ac572138dd40ae61. Final Node 20 results: A1H 8/8; Strava 711/711; full Functions 7,514 pass plus 64 intended skips; frontend 951/951; Rules 418/418; commerce emulator 63/63; SPA 11/11; static workflow/safety 93/93; Functions and frontend lint, TypeScript, diagnostic production build, and diff checks passed.
  • Dependency audit: no dependency changed. Root audit remains 59 total with no critical; root production remains 4 with no critical. Functions full and production audits remain 8 moderate and no high or critical. These findings remain unresolved and are not accepted as safe.
  • Independent review: runtime/security GO; test/mock GO; corrected backup-officer/documentation GO.
  • Code merged: not yet.
  • Website published: not relevant; no website source changed.
  • Netlify intended commit verified: not relevant.
  • runmprc.com verified: not relevant.
  • Firebase deployed: not yet; no deploy attempted.
  • Outside provider configured: not relevant; no provider setting changed.
  • Outside provider verified: not yet; no Strava request or real account was used.
  • Production behavior verified: not yet.

Residual risk

Provider refresh calls remain unserialized. The three-field fence does not detect an identical-tuple replacement. Post-commit acknowledgement can remain ambiguous, and provider/local reconciliation, IAM, encryption, audit, protected deployment, and live verification stay open under #88.

Safety review

  • No secrets, recovery codes, private member data, or payment data are included.
  • Planned behavior is marked NOT AVAILABLE YET through explicit source-only and not-deployed language.
  • Skipped checks and deployments are reported as incomplete, not green or live.
  • No page, data movement, permission topology, or deployment diagram changed; no Mermaid update is required.
  • No officer procedure changed; this server-only specialist boundary remains explicitly source-only.

@netlify

netlify Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for luminous-fox-7c393f ready!

Name Link
🔨 Latest commit 49e81b5
🔍 Latest deploy log https://app.netlify.com/projects/luminous-fox-7c393f/deploys/6a6ee058f96754000863d48c
😎 Deploy Preview https://deploy-preview-607--luminous-fox-7c393f.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@daliu

daliu commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Self-review: GO — no remaining findings on exact remote head 49e81b5.

  • Scope: exactly SECURITY.md plus the named refresh runtime and test/mock hunks; no overlap with active email-verification work or draft rollback PR Rollback: prepare exact pre-header Netlify artifact #462.
  • Authorization and App Check: existing callable admission is unchanged and still precedes connection/secret work.
  • Concurrency and state transitions: one read-before-write transaction reprojections the stored tuple on every attempt; only literal exact equality stages the merge; changed, missing, malformed, or retried-to-changed state cannot use the stale bearer downstream.
  • Retry/idempotency: the provider call remains outside the transaction; transaction callback retries do not repeat provider work. A same-tuple retry can restage the same credentials with a later timestamp. Exact-tuple ABA and provider-call serialization remain explicit residuals.
  • Failures: reference, start, read, timestamp, set, commit, pre-apply write, and post-apply acknowledgement failures map to the existing fixed internal refresh result. No caught value is bound, inspected, formatted, serialized, or logged.
  • Privacy/secrets: no raw token, provider response, request body, URL, member data, or failure detail is logged or returned. Scope and unknown stored fields are merge-preserved.
  • Input bounds: existing safe token and positive-safe expiry projection remains the only tuple source; no coercion or accessor path was added.
  • Migration and compatibility: no schema or dependency change. Fresh-token reads and successful refresh results remain compatible. Existing transaction-delete mock behavior and the full Strava suite pass.
  • Deployment fallback: no website, Firebase, or provider mutation. Undo is a reviewed revert of the exact commit before any separately approved protected Function release.
  • Documentation: the corrected RISK-024 row distinguishes repository source, deployment, provider, data, and live evidence; no officer procedure or Mermaid surface changed.

Independent runtime/security, test/mock, and corrected backup-officer reviews are GO. Patch SHA-256: bc2c962c56329bef7aa5664542c71b34c05d19a03f439c769910a25c26dcfef5.

@daliu

daliu commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Hosted gate: GO on unchanged head 49e81b5 and unchanged base a62c147.

  • PR CI run 30735558743 passed all five jobs, including each named step.
  • Netlify deploy-preview 6a6ee058f96754000863d48c is ready for exact commit 49e81b5 on this branch, returns HTTP 200 with x-robots-tag: noindex, and has published_at null.
  • GitHub deployments for the exact head are [].
  • No website production publication, runmprc.com revision verification, Firebase deployment, Strava provider configuration/request, production-data action, or live behavior occurred.
  • PR remains mergeable; GitHub reports review required because the author cannot approve the same PR. User authorization, exact self-review, and three independent read-only GO reviews support the admin merge path.

@daliu
daliu merged commit 24ec0e5 into main Aug 2, 2026
9 checks passed
@daliu
daliu deleted the codex/issue-606-strava-refresh-cas branch August 2, 2026 06:18
@daliu

daliu commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

FINAL: merged and exact-main verified.

  • Merge commit: 24ec0e5
  • Parents: a62c147 and reviewed head 49e81b5
  • Tree: 6b3a0db31e87ca7e247c92835f4ccd1a575e0bbf
  • PR CI 30735558743: all five jobs passed.
  • Exact-main CI 30735667378: all five jobs passed.
  • Preview 6a6ee058f96754000863d48c: ready, exact head, noindex, unpublished.
  • Exact-main Netlify production attempt 6a6ee128588b0c0008dec7b8: error, published_at null.
  • GitHub deployments for exact main: [].
  • Live runmprc.com marker remains WEB-002A — Publish bounded Shop and Events permissions containment #473 control/source/tree, not this merge.
  • No Firebase deployment, Strava provider action, production-data action, or live behavior verification.

Issue #606 is complete/closed and the claim is released. Residual #88 work remains explicitly open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OAUTH-001A1H — Refuse stale Strava refresh persistence

1 participant