Skip to content

fix: validate OAuth state on the web-flow callback - #3444

Merged
setchy merged 1 commit into
mainfrom
fix/validate-oauth-web-flow-state
Oct 17, 2026
Merged

setchy merged 1 commit into
mainfrom
fix/validate-oauth-web-flow-state

Conversation

@setchy

@setchy setchy commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

What

Generate a per-attempt, cryptographically random OAuth state for the GitHub OAuth App web-flow login, and accept a gitify://oauth callback only when it echoes that state.

The gitify:// custom-protocol handler is registered at the OS level and can be triggered from a webpage, an email, or another local app. Previously performGitHubWebOAuth() never sent a state and handleCallback() never checked one, so any authorization code delivered through gitify://oauth?code=... was accepted and exchanged for a token. An attacker could complete GitHub's authorize step for their own account and have the code delivered to a victim's running Gitify, silently binding the victim's client to the attacker's account (login-CSRF, CWE-352).

Fixes #3425. Supersedes #3424 — thanks to @carfeii for reporting the issue and proposing the original fix; this branch re-implements that approach on main.

Changes

  • performGitHubWebOAuth() generates one random state per attempt (via generateOAuthState()) and passes it to getWebFlowAuthorizationUrl; it reads the state back from the result so the value compared on callback is exactly the one embedded in the authorization URL.
  • handleCallback() compares the callback's state with the attempt's: a missing or mismatched state is logged and ignored, so it does not resolve, reject, or tear down the still-pending legitimate login.
  • The same check gates callback error responses, so an unauthenticated gitify://…?error=… delivery can no longer abort a pending login.
  • An unparseable gitify:// callback URL (e.g. gitify://[, which passes the protocol handler's prefix check) is likewise logged and ignored instead of rejecting, so it cannot abort a pending login before the state is compared.
  • Adds generateOAuthState() in src/renderer/utils/auth/state.ts, backed by crypto.randomUUID().
  • Tests: the success flow echoes the state; regression tests prove injected, mismatched, and unparseable callbacks neither resolve nor disturb the pending login.

Testing

  • pnpm test — 190 files, 1913 tests pass
  • pnpm lint:check and pnpm check — clean

Not changed

The device flow, PAT login, CLI login, and non-GitHub forges are unchanged.

@setchy
setchy requested a review from afonsojramos as a code owner October 16, 2026 21:44
@github-actions github-actions Bot added the bug Something isn't working label Oct 16, 2026
@setchy
setchy force-pushed the fix/validate-oauth-web-flow-state branch 3 times, most recently from d1ea458 to b9c6c73 Compare October 17, 2026 02:20
Generate a per-attempt random `state` for the GitHub OAuth App web-flow
login and accept a `gitify://oauth` callback only when it echoes that
state. A callback carrying a missing or mismatched state is logged and
ignored, so an injected callback can neither bind the client to another
GitHub account (login-CSRF) nor tear down the pending login.

Treat an unparseable `gitify://` callback URL the same way: log and ignore
instead of rejecting, since a forged URL (e.g. `gitify://[`) passes the
protocol handler's prefix check but cannot carry a valid state and would
otherwise abort a legitimate login.

Generate the state with `crypto.randomUUID()` and cover it with unit tests.
@setchy
setchy force-pushed the fix/validate-oauth-web-flow-state branch from b9c6c73 to b813909 Compare October 17, 2026 02:23
@sonarqubecloud

Copy link
Copy Markdown

@setchy
setchy merged commit 10235b2 into main Oct 17, 2026
23 checks passed
@setchy
setchy deleted the fix/validate-oauth-web-flow-state branch October 17, 2026 02:27
@github-actions github-actions Bot added this to the Release 7.10.0 milestone Oct 17, 2026
@setchy

setchy commented Oct 17, 2026

Copy link
Copy Markdown
Member Author

After a little more thought, i think we can retire this whole OAuth flow completely. PR cooking 👨‍🍳

@setchy

setchy commented Oct 17, 2026

Copy link
Copy Markdown
Member Author

#3449

@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in Gitify v7.10.0! 🎉

Downloads for macOS, Windows, and Linux are available on the release page.

Thanks for helping make Gitify better!

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

Labels

bug Something isn't working

Development

Successfully merging this pull request may close these issues.

Missing OAuth state validation allows login-CSRF via the gitify:// callback

1 participant