Skip to content

Fix: verify gist scope on connect (#195 bug 4)#201

Merged
tylergraydev merged 1 commit into
mainfrom
fix/verify-gist-scope
Apr 19, 2026
Merged

Fix: verify gist scope on connect (#195 bug 4)#201
tylergraydev merged 1 commit into
mainfrom
fix/verify-gist-scope

Conversation

@tylergraydev

Copy link
Copy Markdown
Owner

Summary

Closes the last remaining bug from #195: connect_cloud_sync accepted any token gh auth token returned, so users without the gist scope got a generic 403 on first push instead of a helpful message at connect time.

Now verifies the gist scope via the X-OAuth-Scopes response header on GET /user before storing credentials. If the scope is absent, the error names the exact remediation: `gh auth refresh -h github.com -s gist`.

Fine-grained PATs don't emit X-OAuth-Scopes, so in that case we fall through (the API-level error will still surface later on the actual gist call).

What changed

  • services/gist_sync.rs — added verify_gist_scope(token) + 4 tests (scope present / scope missing / header absent / 401).
  • commands/cloud_sync.rs — call verify_gist_scope in connect_cloud_sync after get_authenticated_user, before find_or_create_gist.
  • Cargo.lock — refresh to match Cargo.toml version (was stale at 3.8.1, now 3.8.3).

Test plan

  • `cargo test --lib gist_sync` — 14/14 pass (4 new).
  • `cargo fmt --check` clean.
  • Manual: disconnect gh, re-auth without gist scope (`gh auth refresh -s repo`), click Connect — should see the scope error with remediation, not a 403 later.

Bugs 1–3 from #195 already landed

  • Bug 1 (pagination): commit `511c98c`
  • Bug 2 (disconnect empty-string check): commit `815cf4c`
  • Bug 3 (backup extension): commits `1c55739`, `f74448e`

Once this merges, #195 can close.

🤖 Generated with Claude Code

#195)

If a user authenticated gh for Git only, connect_cloud_sync accepted the
token and every subsequent Gist API call returned 403 with a generic
failure. Verify the 'gist' scope via the X-OAuth-Scopes header on /user
before storing credentials, so the error names the exact fix:
gh auth refresh -h github.com -s gist.

Fine-grained PATs don't emit X-OAuth-Scopes, so their absence falls
through (no upfront verification possible — API-level errors still
surface later). Added 4 tests covering scope present, scope missing,
header absent (fine-grained PAT), and 401.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tylergraydev

Copy link
Copy Markdown
Owner Author

Second-pass review (independent agent)

Verdict: good to merge as-is. No high-confidence issues flagged.

Header-absent fall-through is correct. Classic OAuth/PAT tokens always emit X-OAuth-Scopes (even when empty); fine-grained PATs and GitHub App installation tokens don't. Falling through on absence is right — blocking would break fine-grained PAT users, and the API-level 403 still surfaces on the actual gist call.

Error message reaches the user verbatim. The call site maps verify_gist_scope's error as-is via e.to_string() (unlike get_authenticated_user, which wraps in "Failed to validate token: {}"), so the remediation command lands cleanly in the UI.

No security concerns. Token flows via existing auth_headers() helper, never logged. to_str().unwrap_or("") degrades gracefully on non-ASCII headers (no panic). Exact-match == "gist" avoids substring false positives. Lowercase "x-oauth-scopes" is correct — reqwest's HeaderMap is case-insensitive per HTTP spec.

Test coverage sufficient. 4 tests cover scope-present, scope-missing (asserts both message fragments), header-absent (fine-grained PAT), and 401. No gaps worth closing.

Style consistent. verify_gist_scope mirrors get_authenticated_user — same URL/headers/error shape. let Some(...) = ... else is idiomatic.

Minor note (not blocking)

Both service calls in connect_cloud_sync hit GET /user. A single call returning both login and scopes would save one request, but splitting keeps the methods independently testable/reusable — better tradeoff for a once-per-connect flow.

@tylergraydev tylergraydev merged commit 92e124e into main Apr 19, 2026
10 checks passed
@tylergraydev tylergraydev deleted the fix/verify-gist-scope branch April 19, 2026 22:53
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.

1 participant