Skip to content

Fix cross-process Linear OAuth refresh wiping valid connections#400

Draft
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-correctness-bugs-0bf2
Draft

Fix cross-process Linear OAuth refresh wiping valid connections#400
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-correctness-bugs-0bf2

Conversation

@cursor
Copy link
Copy Markdown
Contributor

@cursor cursor Bot commented May 29, 2026

Bug and impact

When ADE desktop and ade serve (headless runtime) are both active for the same project, a near-expiry Linear OAuth refresh can race. Linear rotates the refresh token on the first successful exchange; the second runtime retries with the now-invalid refresh token, gets invalid_grant, and clears the shared credential store — forcing the user to reconnect Linear even though the connection was still valid.

This was introduced by the automatic OAuth token refresh added in #395.

Root cause

  • ensureFreshToken() deduped refreshes in-process only (refreshInFlight).
  • Desktop and headless each use the shared EncryptedFileCredentialStore under .ade/secrets.
  • On invalid_grant, both services unconditionally cleared all Linear credentials.

Fix

  • Add a cross-process file lock (linear-oauth-refresh.lock) so only one runtime refreshes at a time.
  • Re-read the credential store after invalid_grant; if the refresh token was rotated or the access token is now fresh, treat the failure as a stale race and do not clear the connection.
  • Apply the same logic to desktop (linearCredentialService) and headless (headlessLinearServices).

Validation

  • npm run test -- --run src/main/services/cto/linearTokenRefresh.test.ts src/main/services/cto/linearAuth.test.ts (45 tests passed)
  • npm --prefix apps/ade-cli run test -- --run src/headlessLinearServices.test.ts (22 tests passed)
  • npm --prefix apps/desktop run typecheck
Open in Web View Automation 

When desktop and ade serve both refresh near token expiry, Linear rotates
the refresh token on the first exchange. The loser gets invalid_grant and
was clearing the shared credential store, forcing a full reconnect.

Serialize refresh with a cross-process lock under .ade/secrets and treat
invalid_grant as stale when the store already has a rotated refresh token
or a fresh access token.

Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview May 29, 2026 2:12pm

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