Skip to content

fix(auth): stop double deep-link delivery wiping auth on macOS Tahoe#1895

Merged
richiemcilroy merged 1 commit into
CapSoftware:mainfrom
ManthanNimodiya:fix/auth-double-delivery-loop
Jun 7, 2026
Merged

fix(auth): stop double deep-link delivery wiping auth on macOS Tahoe#1895
richiemcilroy merged 1 commit into
CapSoftware:mainfrom
ManthanNimodiya:fix/auth-double-delivery-loop

Conversation

@ManthanNimodiya
Copy link
Copy Markdown
Contributor

@ManthanNimodiya ManthanNimodiya commented Jun 7, 2026

Summary

On macOS Tahoe, the OS delivers the OAuth callback twice. When the deep-link wins the race, the local server stayed alive for 10s, the second delivery raced with updateAuthPlan and wiped the auth store, looping back to login.

Changes

  • auth.ts — dispose both listeners immediately when either wins the race
  • lib.rs — stop update_auth_plan from clearing auth on plan fetch failure

Test plan

  • Sign in on macOS Tahoe — auth persists after quit + reopen
  • Sign in on cap.so — unaffected
  • Sign in on self-hosted — needs self-hosted setup to verify

Greptile Summary

Fixes a macOS Tahoe bug where the OS delivers the OAuth deep-link callback twice: the first delivery set the auth store, but the 10-second delayed disposal of the local OAuth listener let the second delivery race with updateAuthPlan and clear the auth, looping users back to login.

  • auth.ts: Both the deep-link and local-callback listeners are now disposed immediately once either one wins the Promise.race, so a second OS delivery has nowhere to land.
  • lib.rs: Removes the AuthStore::set(None) that was called whenever AuthStore::get returned Err or Ok(None) in check_upgraded_and_update; the function now just returns Ok(false), preserving any valid auth already in the store.

Confidence Score: 4/5

Both changes are targeted and correct; the only leftover is a dead source field in the race wrapper that is cosmetic.

The auth race fix correctly disposes both listeners immediately, and the Rust change removes the destructive store-clear on a transient auth-read failure. The source wrapper object left over in the TypeScript race is dead code but has no runtime impact.

No files require special attention beyond the minor dead-code cleanup in auth.ts.

Important Files Changed

Filename Overview
apps/desktop/src/utils/auth.ts Removes the 10 s delayed disposal of the local OAuth listener; both listeners are now disposed immediately after either one wins the race, preventing a second OS delivery from ever reaching processAuthData. The source wrapper object is now dead code but otherwise the fix is correct.
apps/desktop/src-tauri/src/lib.rs Removes the destructive AuthStore::set(None) that was triggered whenever AuthStore::get returned Err or Ok(None); function now simply returns Ok(false) in that branch, preserving any valid auth already in the store.

Comments Outside Diff (1)

  1. apps/desktop/src/utils/auth.ts, line 86-95 (link)

    P2 Dead source field after the race simplification

    The source: "deep-link" | "local" wrapper is now unused — the only consumer was the if (result.source === "deep-link") branch that this PR removed. The Promise.race can be simplified to resolve directly to AuthParams | null (i.e. await Promise.race([deepLink.complete, localCallback.complete])), and result.data becomes just result.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/utils/auth.ts
    Line: 86-95
    
    Comment:
    **Dead `source` field after the race simplification**
    
    The `source: "deep-link" | "local"` wrapper is now unused — the only consumer was the `if (result.source === "deep-link")` branch that this PR removed. The `Promise.race` can be simplified to resolve directly to `AuthParams | null` (i.e. `await Promise.race([deepLink.complete, localCallback.complete])`), and `result.data` becomes just `result`.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/desktop/src/utils/auth.ts:86-95
**Dead `source` field after the race simplification**

The `source: "deep-link" | "local"` wrapper is now unused — the only consumer was the `if (result.source === "deep-link")` branch that this PR removed. The `Promise.race` can be simplified to resolve directly to `AuthParams | null` (i.e. `await Promise.race([deepLink.complete, localCallback.complete])`), and `result.data` becomes just `result`.

Reviews (1): Last reviewed commit: "fix(auth): stop double deep-link deliver..." | Re-trigger Greptile

@richiemcilroy richiemcilroy merged commit 4878653 into CapSoftware:main Jun 7, 2026
15 of 17 checks passed
@ManthanNimodiya
Copy link
Copy Markdown
Contributor Author

@richiemcilroy that was lightning fast mate

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.

2 participants