Repository navigation
STRAVA-001 — Harden and modernize the existing Strava OAuth connection #88
Description
Problem
The repository has a functional Strava prototype, but OAuth state is browser-local, provider error bodies can leak, scopes are broader than club-link needs, token refresh is not concurrency safe, and provider capacity/configuration is unverified.
Scope
- Use server-issued, UID/session-bound, one-use OAuth state with TTL.
- Bound callback/code inputs and return generic client errors without provider bodies.
- Request only read for club-link use; make activity:read a separate explicit feature consent and persist actual granted scopes.
- Enforce one athlete ID owner; keep tokens server-only; make refresh rotation transactional/idempotent.
- Adopt the current OAuth revoke path and retry-safe deletion behavior.
- Verify provider athlete capacity and use non-production credentials/accounts for tests.
Acceptance criteria
- CSRF/state replay, wrong/missing scope, duplicate athlete, concurrent refresh, revoke retry, rate limit, and redaction tests pass.
- Disconnect/deauthorization removes local tokens and connection data according to policy.
- No Strava token/provider body is exposed to clients, logs, custom claims, or issues.
- Local/CI cannot call the production Strava host.
- The integration never grants website membership or discounts.
Dependencies and sequence
- SAFETY-001/SAFETY-001 — Preserve callbacks and fail closed on local Firebase isolation #99 is the callback/privacy foundation and is complete in source. OAUTH-001C depends on that guard.
- OAUTH-001A follows SEC-001 and the AUTH-003 capability boundary. Production cutover still requires the exact Firebase deployment evidence owned by CI-001 — Establish trustworthy quality gates and protected deployment #105.
- AUTH-001 remains a live-rollout gate for associating Strava with a verified website account; it does not block isolated synthetic A/B/C implementation and tests.
- ABUSE-001A is not a blanket prerequisite. It may enforce App Check on its agreed callable group while explicitly deferring
stravaExchangeCode. OAUTH-001C then proves the safe handoff before that one callable moves into enforcement. - OAUTH-001B follows A and requires the named provider/IAM/encryption owner decisions before hosted closure.
Official references
- https://developers.strava.com/docs/authentication/
- https://developers.strava.com/docs/rate-limits/
- https://www.strava.com/legal/api_policy
Coordination
Parent: #79
Read the slice-specific sequence below before claiming. Do not claim the whole tracker unless one reviewable outcome truly completes A, B, and C. A or C may be assigned and claimed independently after only that slice's dependencies are complete; C follows merged #99 and the recorded ABUSE-001A deferral, not completion of all ABUSE work. B is owner action for provider/IAM/encryption decisions and follows A. Before editing, assign the exact slice and post a timestamped CLAIMED comment naming the slice, agent, and branch. Creation of this tracker is not a claim. Preserve parallel work tracked by stable IDs in GITHUB_ISSUES.md.
Canonical OAUTH-001 map — 2026-07-13
This issue is the canonical live tracker for repository outcome OAUTH-001. The bounded OAUTH-001A/B/C rows in GITHUB_ISSUE_SLICES.md are parts of this issue, not separate tickets to publish by default.
- OAUTH-001A — refresh concurrency: implement server-only transaction/version-safe refresh so a retry or concurrent call cannot overwrite a newly rotated refresh token.
- OAUTH-001B — scope, revoke, storage, and audit: minimize and verify scopes/account binding; implement retry-safe disconnect/provider revocation; record the named IAM/application-encryption decision; add redacted durable access/refresh/failure/revoke audit.
- OAUTH-001C — protected callback handoff: preserve SAFETY-001 — Preserve callbacks and fail closed on local Firebase isolation #99's initial capability guard; keep OAuth
code/stateout of third-party startup; retain one-use UID/session-bound state verification and server-only exchange; keepstravaExchangeCodeexcluded from App Check enforcement until the handoff is proven; then test missing/invalid/valid App Check plus encoded, case, and trailing-slash callback variants.
Dependencies and gates:
- A follows SEC-001 and the AUTH-003 capability boundary.
- B follows A and requires named owner review for provider configuration plus IAM/encryption policy.
- C follows SAFETY-001 — Preserve callbacks and fail closed on local Firebase isolation #99. ABUSE-001A must defer
stravaExchangeCodeuntil C's safe handoff and callback regression evidence merge. - No test may use a real member token or the production Strava host.
- None of these outcomes grants website membership, dues status, discounts, or admin access.
Split/claim rule: do not publish a duplicate OAUTH/Strava ticket. A and C are proposed implementation slices; B remains owner action because it contains provider/IAM/encryption decisions. If one pull request cannot safely review all three outcomes, keep #88 as the tracker, create and cross-link one atomic child first, then assign and post a timestamped CLAIMED marker for that child before editing.
Activity
docs: map OAuth slices to live issue #88 (#129)
daliu commentedon Jul 13, 2026
Canonical mapping evidence (2026-07-13): PR #129 merged to final main as f6e86be. This issue body and the root OAUTH-001/OAUTH-001A/B/C maps now agree on P0 labels, A/C proposed, B owner_action, slice-specific dependencies, ABUSE deferral/cutover, App Check callback evidence, and the no-duplicate split/claim rule. #88 remains open, unassigned, and unclaimed; do not publish a parallel Strava/OAUTH ticket without first making #88 the tracker and cross-linking one atomic child.
daliu commentedon Jul 14, 2026
Atomic child #223 published for only the server authorization-exchange redaction in
functions/strava.jsplus a new focused Functions test. It does not complete OAUTH-001C and leaves all callback/state/App Check/refresh/scope/revoke/IAM/provider/deployment work here.daliu commentedon Jul 14, 2026
OAUTH-001C1A child #223 is COMPLETE and its exact two-path claim is RELEASED.
PR #225 squash-merged reviewed head
51e46b34e0a71bf2976d34dd4fb1bbd8bec773b4as exact mainff8e9068d8a01f7512d0119e675f9b5df6c69a3a. PR CI 29307783519 and post-merge CI 29307918575 passed all five required jobs. The child redacts authorization-exchange failures while preserving the successful server-only contract.This was source/tests only: no protected release, Firebase deployment, Strava/provider call or configuration, production-data action, migration, production-site publication, or live-behavior proof.
Canonical #88 remains open for bounded/one-use code and state, token/scope/account validation, native App Check handoff, refresh redaction/concurrency, disconnect/revoke/audit, encryption/IAM, provider capacity/configuration, Firebase deployment, and live proof.
daliu commentedon Jul 14, 2026
Atomic refresh-redaction child #229 is published, assigned, and CLAIMED as OAUTH-001A1 by /root at 2026-07-14T05:34:34Z on
codex/issue-229-strava-refresh-redaction, exact base35ca312e285417d7d84c67d45a7d1bec01860372.This is not a duplicate tracker and does not complete OAUTH-001A. It owns only the existing
refreshTokenfailure hunk infunctions/strava.jsplus focused synthetic support/tests infunctions/strava.test.js. It removes provider status/body/raw-exception exposure while preserving successful behavior. Refresh concurrency/versioning, capability work, validation, scopes, revoke/audit, encryption/IAM, provider setup, deployment, and live proof remain here.daliu commentedon Jul 14, 2026
#229 / PR #231 is the claimed atomic OAUTH-001A1 refresh-error-redaction child. It is rebased onto exact main
6f36953c3668c45b653abc28593a266568de37db; exact scope remains onlyfunctions/strava.jsrefresh failure handling and focusedfunctions/strava.test.jscoverage. Hosted CI is in progress. Full OAUTH-001A concurrency/capability work and all other #88 residuals remain open and are not advanced by this child.fix: redact Strava token refresh failures (#231)
65 remaining items
daliu commentedon Aug 2, 2026
ATOMIC CHILD CLAIMED: #604 / OAUTH-001A2J by
/rooton branchcodex/issue-604-strava-response-status, exact basedff1a40194bd1f25938ac16564a6ce27bbb9eab5. Scope is only safe admission of Strava HTTPokevidence by generalizing the existing disconnect helper across exchange, refresh, activities, and statistics plus named tests. It does not claim normal wire-byte exploitability, response-byte streaming, refresh concurrency, provider governance, deployment, or completion of #88.daliu commentedon Aug 2, 2026
OAUTH-001A2J / #604 COMPLETE and CLAIM RELEASED. PR #605 merged exact reviewed head 604e51c as exact main a62c147. Exact-main CI 30734583158 passed all five required jobs.
Officer impact: no current visible change; safe Strava HTTP success admission becomes relevant only after a future protected Firebase Functions release.
Officer documentation: None — server-only containment; existing SOURCE ONLY / NOT LIVE guide remains accurate.
Deployment evidence: unpublished exact-head preview only; exact-main Netlify production attempt errored unpublished; GitHub deployments are empty; live marker remains #473; no Firebase deployment, provider action, production data, or live verification. All #604 claims are released.
daliu commentedon Aug 2, 2026
Atomic child #606 is CLAIMED by /root at 2026-08-02T05:59:37Z on exact base a62c147.
Scope is only stale Strava refresh compare-and-set persistence in functions/strava.js, its focused transaction/test mock hunks in functions/strava.test.js, and one named SECURITY.md current-truth hunk. This does not claim or close #88 and excludes provider-call serialization, response byte limits, client disconnect behavior, deployment, provider configuration, real data, and live verification.
Claim marker: #606 (comment)
daliu commentedon Aug 2, 2026
OAUTH-001A1H child #606 is COMPLETE / CLOSED / CLAIM RELEASED at exact main 24ec0e5 through PR #607. PR CI 30735558743 and exact-main CI 30735667378 passed all five jobs. No Firebase, provider, production-data, or live deployment claim.
The merged source transactionally fences a refresh response on the exact access/refresh/expiry tuple. Provider-call serialization, identical-tuple ABA, acknowledgement/provider reconciliation, IAM/encryption/audit, protected deployment, and live proof remain open; #88 stays open.
daliu commentedon Aug 2, 2026
Atomic P0 child #608 (
OAUTH-001B9 — Refuse stale Strava disconnect cleanup) is now claimed on exact main24ec0e5789c0ff26cba49371d298aea5c0cdf3ce.It owns only the server disconnect snapshot/version compare-before-delete transaction, focused Functions tests/mock support, and the existing RISK-024 current-truth row. The separate client fulfilled-response validator is reserved for a later B10 child. No source result, merge, Firebase deployment, provider action, production-data action, or live behavior is claimed by this coordination marker.
OAUTH-001B9 — Refuse stale Strava disconnect cleanup (#609)
daliu commentedon Aug 2, 2026
OAUTH-001B9 child #608 is COMPLETE / CLOSED / CLAIM RELEASED via PR #609. Exact release SHA:
9d8bfaae0a4f9e573053b4035954e933ae626875. PR and exact-main CI passed all five jobs; reviewed/merge trees are identical. This is merged source only: Firebase and Strava were not deployed or exercised, the main Netlify attempt remained unpublished, and live behavior is unverified. Parent #88 remains open for the remaining OAuth work.daliu commentedon Aug 2, 2026
ATOMIC CHILD CLAIMED: #610 / OAUTH-001B10 by
/rooton branchcodex/issue-610-strava-disconnect-confirmation, exact base9d8bfaae0a4f9e573053b4035954e933ae626875. It owns only the exact client fulfilled-confirmation admission, focused frontend tests, RISK-024 current truth, and the existing no-terminal disconnect procedure hunk. It does not claim provider revocation proof, Functions/server state, reconciliation, audit, IAM/encryption, deployment, live behavior, or completion of #88.OAUTH-001B10 — Require exact Strava disconnect client confirmation (#611
daliu commentedon Aug 2, 2026
OAUTH-001B10 child #610 is COMPLETE / CLOSED / CLAIM RELEASED.
PR #611 merged as exact main
63d29bedf379a857421501a5c0938c2901618095; reviewed head and merge share treea4c23c8b69a6076f01c3f4be3f06efed50fa9a72. PR CI 30737652078 and post-main CI 30737752006 passed all five jobs. No GitHub deployment exists; Netlify main failed unpublished and the live marker remains #473, so no live-production claim is made. The remote branch, assignee, and in-progress label are cleared.daliu commentedon Aug 2, 2026
ATOMIC CHILD CLAIMED: #612 / OAUTH-001C1J by /root at 2026-08-02T07:34:49Z on branch
codex/issue-612-strava-exchange-confirmation, exact base63d29bedf379a857421501a5c0938c2901618095.Scope is only exact browser admission of the existing server exchange confirmation, focused frontend tests, RISK-009 current truth, and the existing callback-failure officer procedure. It does not claim scope/account policy, Functions behavior, provider/local reconciliation, audit, IAM/encryption, provider configuration, deployment, live proof, or completion of #88.
daliu commentedon Aug 2, 2026
OAUTH-001C1J #612 — COMPLETE / CLOSED / CLAIM RELEASED
PR #613 merged as exact main
644d9f1fe3e6c06427412fd3ae37973dbbfeca20(tree031b49e53a5b728944c2abeac18d58ffcd6f1a6d). Exact-main CI30738868932passed all five jobs; GitHub deployments are[]. The main Netlify attempt6a6ef8d14154fc0008c2aabbfailed unpublished, leaving #473 control deploy6a6dc9ea588b0c0008036312live. No Firebase/provider/data/live action or #612runmprc.comverification occurred.The exact-confirmation client boundary is complete; response bounds/timeouts, scope/account policy, reconciliation, audit, IAM/encryption, deployment, and live evidence remain open under #88.
#612 claim released at
644d9f1fe3e6c06427412fd3ae37973dbbfeca20; its four paths are unowned again.daliu commentedon Aug 2, 2026
OAUTH-001A2K #614 — CLAIMED after reviewable split
Exact base:
644d9f1fe3e6c06427412fd3ae37973dbbfeca20.This child now owns only the 64 KiB native streamed-body boundary for authorization-exchange and refresh token JSON replies in
functions/strava.js, focused/preserved tests infunctions/strava.test.js, and one separateSECURITY.mdrow. The pre-implementation four-response concept was split: activities/statistics response caps and targeted unread-sibling cleanup remain unclaimed for a later #88 child because their compatibility and resource lifecycle differ.No browser/officer doc, disconnect/revoke, package/workflow, Rules/schema, timeout/abort, Firebase deployment, provider, production-data, or live path is claimed.
daliu commentedon Aug 2, 2026
OAUTH-001A2K / #614 COMPLETE, CLOSED, CLAIM RELEASED
PR #615 merged as exact main
b94622bd42775d84704d5fc05ee87da81f5a76e0(reviewed tree3683a9ccb982b6a2bb1c67a8e0fd27a86f38bcc9). PR CI30740379852and exact-main CI30740495924each passed all five jobs; all exact-head reviews were GO.This child bounds authorization-exchange and refresh JSON responses to 65,536 content-decoded bytes / 4,096 chunks before parse or persistence. It does not deploy Firebase or the website, configure/contact Strava, inspect production data, or prove live behavior. Exact-main GitHub deployments are empty; only the exact-head unpublished Netlify preview is ready.
The claim on
functions/strava.js,functions/strava.test.js, and the named OAUTH-001A2KSECURITY.mdrow is released atb94622bd42775d84704d5fc05ee87da81f5a76e0.A later separately named child may own activities/statistics response limits plus targeted cleanup of the concurrently started sibling body. That outcome remains unclaimed here and must preserve #571 and all other active claims.
Full closure evidence: #614 (comment)
daliu commentedon Aug 2, 2026
ATOMIC CHILD CLAIMED: #616 / OAUTH-001A2L by
/rootat2026-08-02T09:08:26Zon branchcodex/issue-616-strava-data-response-bytes, exact baseb94622bd42775d84704d5fc05ee87da81f5a76e0.It owns only bounded native JSON adoption for the existing recent-activities/statistics GET responses, targeted cleanup of the unread successful statistics sibling after post-fulfillment activity failure, focused/preserved Functions tests, and one named
SECURITY.mdrow. The chosen 1 MiB/64 KiB limits are explicit MPRC application budgets, not Strava guarantees.It excludes Promise-all transport-rejection sibling acquisition/timeouts, non-success-body cleanup, token paths, browser/callback/disconnect, scope/account/reconciliation/audit/IAM/encryption, #571, packages/workflows/Rules/schema, Firebase/provider configuration, production data, deployment, live proof, and completion of #88.