Skip to content

Comments

Handle callback error on link#971

Merged
elie222 merged 1 commit intomainfrom
fix/callback-error-toast
Nov 14, 2025
Merged

Handle callback error on link#971
elie222 merged 1 commit intomainfrom
fix/callback-error-toast

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Nov 14, 2025

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability of Google and Outlook account linking by implementing deduplication logic to prevent duplicate linking attempts.
    • Enhanced cookie management to ensure proper cleanup during OAuth callbacks, reducing account linking errors.
    • Strengthened OAuth callback error handling for more consistent failure recovery.

@vercel
Copy link

vercel bot commented Nov 14, 2025

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

Project Deployment Preview Updated (UTC)
inbox-zero Ready Ready Preview Nov 14, 2025 5:26pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 14, 2025

Walkthrough

This PR adds OAuth callback deduplication and result cookie handling for Google and Outlook account linking. It introduces helper functions for building success redirects and detecting duplicate callbacks, updates all linking routes to use these helpers, modifies cookie management to track completed OAuth flows, and extends state parsing utilities. Version bumped to v2.19.3.

Changes

Cohort / File(s) Change Summary
Result Cookie Constants
apps/web/utils/gmail/constants.ts, apps/web/utils/outlook/constants.ts
Added new exported constants GOOGLE_LINKING_STATE_RESULT_COOKIE_NAME and OUTLOOK_LINKING_STATE_RESULT_COOKIE_NAME to store OAuth result states for deduplication.
OAuth Callback Helpers
apps/web/utils/oauth/callback-helpers.ts
New utility module exporting checkOAuthCallbackDedupe (detects and handles duplicate callback requests) and buildOAuthSuccessRedirect (constructs redirects with result cookie encoding). Includes CheckDedupeParams and BuildSuccessRedirectParams types.
OAuth Error Handler
apps/web/utils/oauth/error-handler.ts
Removed response parameter from handleOAuthCallbackError, added optional resultCookieName to ErrorHandlerParams, and refactored to construct redirect locally with cookie cleanup logic.
OAuth State Utilities
apps/web/utils/oauth/state.ts
Added OAuthStateResultCookieValue interface, encodeOAuthStateResultCookie, and parseOAuthStateResultCookie functions for serializing/deserializing result cookies with base64url encoding and validation.
Google Linking Auth-URL
apps/web/app/api/google/linking/auth-url/route.ts
Added deletion of GOOGLE_LINKING_STATE_RESULT_COOKIE_NAME before setting new OAuth state cookie.
Google Linking Callback
apps/web/app/api/google/linking/callback/route.ts
Integrated checkOAuthCallbackDedupe for duplicate detection, replaced manual redirect construction with buildOAuthSuccessRedirect helper, added state validation, and updated cookie cleanup to handle both state and result cookies.
Outlook Linking Auth-URL
apps/web/app/api/outlook/linking/auth-url/route.ts
Added deletion of OUTLOOK_LINKING_STATE_RESULT_COOKIE_NAME before setting new OAuth state cookie.
Outlook Linking Callback
apps/web/app/api/outlook/linking/callback/route.ts
Integrated deduplication check, replaced direct redirects with buildOAuthSuccessRedirect helper, added base redirect URL variable, updated cookie cleanup for both state and result cookies, and refactored error handling.
Version
version.txt
Bumped version from v2.19.2 to v2.19.3.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant OAuth as OAuth Provider
    participant AuthUrl as /auth-url Route
    participant Callback as /callback Route
    participant Helper as Helpers

    User->>AuthUrl: Request OAuth link
    AuthUrl->>AuthUrl: Delete result cookie
    AuthUrl->>AuthUrl: Generate state
    AuthUrl->>User: Set state cookie + OAuth URL
    User->>OAuth: Click link
    OAuth->>Callback: Redirect with code & state
    
    rect rgba(100, 150, 200, 0.2)
        Note over Callback: Deduplication Check
        Callback->>Helper: checkOAuthCallbackDedupe()
        Helper->>Helper: Compare state vs result cookie
        alt Duplicate Detected
            Helper->>Callback: Return redirect with stored params
            Callback->>User: Redirect /accounts
        else New Request
            Callback->>Callback: Continue processing
        end
    end
    
    rect rgba(100, 200, 150, 0.2)
        Note over Callback: Success Path
        Callback->>Helper: buildOAuthSuccessRedirect()
        Helper->>Helper: Encode state + params to result cookie
        Helper->>Callback: Return response with cookies
        Callback->>Callback: Delete state cookie
        Callback->>User: Redirect /accounts (result cookie set)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • Deduplication logic (callback-helpers.ts): Verify that state comparison and cookie matching correctly identify and handle duplicate requests without false positives.
  • Cookie lifecycle management: Ensure result cookies are properly set during success redirects, deleted when necessary, and don't create edge cases with stale cookies.
  • Callback route changes (Google & Outlook): Confirm the integration of checkOAuthCallbackDedupe and buildOAuthSuccessRedirect correctly replaces prior redirect logic and preserves error handling paths.
  • Result cookie encoding/parsing (state.ts): Validate that base64url encoding, JSON serialization, and validation logic handle edge cases (malformed data, missing fields) gracefully.
  • Error handler refactoring (error-handler.ts): Confirm that removing the response parameter and constructing redirects locally doesn't break error message propagation or cookie cleanup.

Possibly related PRs

  • fix: Fix microsoft account link for new accounts #731: Modifies Outlook linking auth-url/callback routes for state handling—shares same route files and could have interaction points with result cookie logic.
  • Simplify merge #969: Updates OAuth linking callback flow and removes action-based state handling—touches identical callback route files and relates to the redirect construction changes.

Poem

🐰 A rabbit hops through state cookies with grace,
Deduping callbacks in cyberspace.
Result cookies stored, redirects now clean,
The smoothest OAuth flow ever seen! 🔐✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Handle callback error on link' is vague and doesn't clearly convey the specific changes made. The PR implements OAuth callback deduplication, cookie management, and refactored redirect handling across multiple OAuth providers. Consider a more specific title that reflects the main change, such as 'Add OAuth callback deduplication and centralize redirect handling' or 'Implement OAuth callback dedupe with result cookie tracking'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/callback-error-toast

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
apps/web/app/api/google/linking/callback/route.ts (1)

4-7: Dedupe + success redirect wiring looks solid; small invariants/nits

The new flow (early checkOAuthCallbackDedupe, then buildOAuthSuccessRedirect with state/result cookies) is coherent and should fix duplicate-callback errors while preserving idempotent UX.

A couple of minor points to consider:

  • receivedState is read twice (for validateOAuthCallback and again at Lines 51-53). Since validation.success implies a valid state, a future refactor could have validateOAuthCallback return the normalized state so you don’t need to re-read and re-guard it here.
  • state is only used for the result cookie, which is fine; just be aware that if validateOAuthCallback’s behavior ever changes, the “Missing state parameter after validation” throw is outside the try/catch and will surface as a 500 via withError rather than going through the OAuth error handler.

Nothing blocking here, just possible polish for future changes.

Also applies to: 13-16, 21-30, 51-58, 168-174, 205-211

apps/web/utils/oauth/error-handler.ts (1)

9-10: Centralized error redirect + cookie cleanup looks good; watch redirectUrl mutation

The updated handleOAuthCallbackError (Lines 9-10,17-18,24-30) correctly:

  • Constructs its own NextResponse.redirect,
  • Always clears the state cookie, and
  • Optionally clears the result cookie when provided, which keeps the dedupe state consistent after failures.

One minor design note: redirectUrl is mutated via searchParams.set. Callers should treat the URL they pass in as per-call and not reuse it elsewhere after this call, otherwise they may see accumulated error/error_description params. If that ever becomes an issue, you could switch the parameter to a string and construct a fresh URL internally.

Also consider whether you truly want to expose raw error.message to the client via error_description; if not, mapping unknown/internal errors to a generic user-facing string while logging the detailed message would tighten things up.

Also applies to: 17-18, 24-30

apps/web/utils/oauth/callback-helpers.ts (1)

9-48: Dedupe helper logic is correct; consider adding observability and clarifying cookie lifecycle

checkOAuthCallbackDedupe (Lines 9-48) correctly:

  • Compares the incoming state against the parsed result cookie,
  • Requires non-empty params before short-circuiting, and
  • Reconstructs the /accounts URL with the cached params while clearing the state cookie.

A couple of optional improvements:

  • Adding a debug/info log when the dedupe path is hit would make it much easier to reason about repeated provider callbacks in production.
  • You currently keep the result cookie after a deduped redirect. That’s sensible for handling multiple quick retries, but if you ever want “use-once” semantics, you could also delete resultCookieName here to avoid long-lived success state. Right now maxAge is already short, so this is non-blocking.

Overall, the dedupe behavior matches the new state/result-cookie design.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5d9c59 and 3b0bb4e.

📒 Files selected for processing (10)
  • apps/web/app/api/google/linking/auth-url/route.ts (2 hunks)
  • apps/web/app/api/google/linking/callback/route.ts (5 hunks)
  • apps/web/app/api/outlook/linking/auth-url/route.ts (2 hunks)
  • apps/web/app/api/outlook/linking/callback/route.ts (5 hunks)
  • apps/web/utils/gmail/constants.ts (1 hunks)
  • apps/web/utils/oauth/callback-helpers.ts (1 hunks)
  • apps/web/utils/oauth/error-handler.ts (1 hunks)
  • apps/web/utils/oauth/state.ts (1 hunks)
  • apps/web/utils/outlook/constants.ts (1 hunks)
  • version.txt (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
apps/web/app/api/outlook/linking/auth-url/route.ts (1)
apps/web/utils/outlook/constants.ts (1)
  • OUTLOOK_LINKING_STATE_RESULT_COOKIE_NAME (2-3)
apps/web/app/api/outlook/linking/callback/route.ts (2)
apps/web/app/api/google/linking/callback/route.ts (1)
  • GET (18-221)
apps/web/app/api/outlook/linking/auth-url/route.ts (1)
  • GET (24-38)
apps/web/utils/oauth/callback-helpers.ts (1)
apps/web/utils/oauth/state.ts (3)
  • parseOAuthStateResultCookie (62-95)
  • encodeOAuthStateResultCookie (56-60)
  • oauthStateCookieOptions (34-40)
apps/web/app/api/google/linking/callback/route.ts (4)
apps/web/app/api/google/linking/auth-url/route.ts (1)
  • GET (31-45)
apps/web/utils/oauth/callback-helpers.ts (2)
  • checkOAuthCallbackDedupe (21-48)
  • buildOAuthSuccessRedirect (62-86)
apps/web/utils/gmail/constants.ts (2)
  • GOOGLE_LINKING_STATE_COOKIE_NAME (16-16)
  • GOOGLE_LINKING_STATE_RESULT_COOKIE_NAME (17-18)
apps/web/utils/oauth/error-handler.ts (1)
  • handleOAuthCallbackError (12-30)
apps/web/app/api/google/linking/auth-url/route.ts (1)
apps/web/utils/gmail/constants.ts (1)
  • GOOGLE_LINKING_STATE_RESULT_COOKIE_NAME (17-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: test
🔇 Additional comments (2)
apps/web/app/api/google/linking/callback/route.ts (1)

127-130: Cookie cleanup behavior is consistent and correctly aligned with new dedupe flow

Clearing both GOOGLE_LINKING_STATE_COOKIE_NAME and GOOGLE_LINKING_STATE_RESULT_COOKIE_NAME:

  • On the linkingResult.type === "redirect" path (Lines 127-130), and
  • In the error handler call (Lines 215-218, via handleOAuthCallbackError)

ensures callers don’t get stuck with stale state/result cookies after a redirect or error, which is important now that result cookies drive deduplication.

Given this, the only remaining source of a result cookie is a successful buildOAuthSuccessRedirect call, which matches the intended “only cache completed flows” behavior.

Also applies to: 215-218

apps/web/utils/oauth/callback-helpers.ts (1)

50-86: Success redirect helper is consistent with state TTL and dedupe design

buildOAuthSuccessRedirect (Lines 50-86) cleanly centralizes the success path:

  • Builds /accounts with arbitrary success params.
  • Deletes the original state cookie.
  • Writes a result cookie (state + params) using oauthStateCookieOptions, giving it the same security flags and TTL as the original state cookie.

This lines up well with checkOAuthCallbackDedupe and the auth-url routes that clear the result cookie when starting a new flow. No issues from a correctness standpoint.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 10 files

@elie222 elie222 merged commit c543ff0 into main Nov 14, 2025
16 checks passed
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