Skip to content

auth: capture Sign in with Apple / OAuth failures instead of swallowing them - #5600

Closed
lawrencecchen wants to merge 2 commits into
mainfrom
feat-siwa-error-capture
Closed

lawrencecchen wants to merge 2 commits into
mainfrom
feat-siwa-error-capture

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 8, 2026 •

Copy link
Copy Markdown
Contributor

Why

Sign in with Apple (and other OAuth) failures were collapsed into the generic "Something went wrong. Please try again." in AuthCoordinator's OAuth catch and captured nowhere. A real auth break was invisible: no Sentry event on macOS, and on iOS the sign-in catch had no error capture at all.

This PR makes that failure path observable. It does not change the thrown error type, so the existing AuthError(displaySafe:) mapping and the iOS SignInView error rendering keep working (no regression).

What

  • AuthErrorReporting seam (injected, NoopAuthErrorReporting default) on AuthCoordinator. Both the native-OAuth catch and the macOS hosted-browser completion path (completeExternalSignIn) report the real, un-wrapped error with the flow + provider before it is mapped to a display-safe AuthError. User cancellation is not reported.
  • macOS Sentry reporter (SentryAuthErrorReporter) wired at the composition root; it extracts the Stack error code (e.g. INVALID_APPLE_CREDENTIALS) and honors the telemetry opt-out. CmuxAuthRuntime stays SDK-free behind the AuthClient seam; iOS keeps the no-op.
  • iOS analytics enrichment: the existing ios_sign_in_failed PostHog event now has distinct failure_reason values for INVALID_APPLE_CREDENTIALS and related OAuth codes, so a structural break is visible in analytics instead of collapsing into a generic oauth_error. Values are enum-like codes, never error text or the user's email.
  • Tests: failed OAuth and failed browser-flow completion report with the right flow/provider; cancellation does not.

Context: the SIWA break (config/architecture, reported separately, not fixed by this PR)

iOS SIWA uses native ASAuthorizationController; Stack validates the Apple identity token's aud (the app bundle id) against the project's registered apple_bundle_ids. Tagged dev builds use per-tag bundle ids (dev.cmux.ios.<tag>) that can never be pre-registered, so native SIWA on dev tagged builds is structurally broken and returns INVALID_APPLE_CREDENTIALS. The Apple client secret is valid (not expired) and TestFlight's bundle (dev.cmux.app.beta) is registered, so those axes are not the cause.

Note: the user-reported symptom was the generic message, but INVALID_APPLE_CREDENTIALS already maps to a specific message on iOS. The generic message only appears for an unrecognized error code, so the user's actual failure is likely a different, still-unidentified code. This PR's capture + PostHog enrichment is what will surface that real code on the next attempt.

Verification

  • iOS simulator build (at HEAD): BUILD SUCCEEDED.
  • macOS tagged build (at HEAD): BUILD SUCCEEDED.
  • swift test (CmuxAuthRuntime): 26/26 pass including the new reporter tests.
  • Autoreview: clean (no actionable findings).

🤖 Generated with Claude Code

lawrencecchen and others added 2 commits June 7, 2026 20:01
Sign in with Apple and other OAuth failures were collapsed into the generic
"Something went wrong. Please try again." message in AuthCoordinator's OAuth
catch and captured nowhere, so a production auth break was invisible.

- Add an injected AuthErrorReporting seam (with NoopAuthErrorReporting default)
  to AuthCoordinator. The OAuth catch reports the real, un-wrapped error with
  the flow + provider before mapping it to a display-safe AuthError. User
  cancellation is not reported. The thrown error type is unchanged, so the
  existing AuthError(displaySafe:) mapping and the iOS SignInView error
  rendering keep working (no regression).
- Wire a Sentry-backed reporter at the macOS composition root
  (SentryAuthErrorReporter) that extracts the Stack error code (e.g.
  INVALID_APPLE_CREDENTIALS) and respects the telemetry opt-out. CmuxAuthRuntime
  stays SDK-free behind the AuthClient seam; iOS keeps the no-op.
- iOS: give the existing ios_sign_in_failed PostHog event distinct
  failure_reason values for INVALID_APPLE_CREDENTIALS and related OAuth codes so
  a structural break (e.g. an unregistered Apple bundle id) is visible in
  analytics instead of collapsing into a generic oauth_error. Values are
  enum-like codes, never the error text or the user's email.
- Tests: a failed OAuth sign-in reports the underlying error with the provider;
  a cancelled sign-in does not report.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…it files

Address autoreview:
- The macOS app signs in through HostBrowserSignInFlow (the web app's hosted
  sign-in), which completes via completeExternalSignIn(), not
  AuthCoordinator.signInWithApple(). Capture session-validation failures there
  too (flow "browser_oauth") so the primary macOS OAuth/Apple path is no longer
  invisible. Cancellation is still skipped. Adds a regression test.
- Split AuthErrorReporting.swift into one-type-per-file (AuthErrorReporting,
  AuthErrorContext, NoopAuthErrorReporting) per the package organization rule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 8, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 8, 2026 3:14am
cmux-staging Building Building Preview, Comment Jun 8, 2026 3:14am

@coderabbitai

coderabbitai Bot commented Jun 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces an error reporting seam for authentication sign-in failures in the Cmux auth framework. A new AuthErrorReporting protocol enables optional injection of error reporters into AuthCoordinator, which now captures and reports OAuth and hosted-browser sign-in failures with flow and provider context. A concrete SentryAuthErrorReporter implementation captures these errors in Sentry while respecting telemetry opt-out settings. Sign-in analytics are also enhanced to distinguish granular OAuth error codes.

Changes

Error Reporting Seam for Authentication Sign-In Failures

Layer / File(s) Summary
Error Reporting Contracts
Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Diagnostics/AuthErrorContext.swift, Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Diagnostics/AuthErrorReporting.swift, Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Diagnostics/NoopAuthErrorReporting.swift
AuthErrorContext struct captures non-sensitive flow and optional provider; AuthErrorReporting protocol defines report(error:context:) interface; NoopAuthErrorReporting provides opt-in default implementation.
AuthCoordinator Error Reporting Integration
Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Coordinator/AuthCoordinator.swift
errorReporter dependency is injected via initializer (defaulting to no-op), stored on coordinator, and called to report OAuth and browser-OAuth sign-in failures with flow/provider context, except for user cancellations.
Test Infrastructure & Coverage
Packages/CmuxAuthRuntime/Tests/CmuxAuthRuntimeTests/Fakes.swift, Packages/CmuxAuthRuntime/Tests/CmuxAuthRuntimeTests/AuthCoordinatorTests.swift
FakeAuthClient gains throwOnOAuth and setThrowOnOAuth(_:) for scripted OAuth failures; FakeAuthErrorReporter captures reports for test assertions; makeCoordinator accepts injected reporter; tests verify OAuth failures report with correct flow/provider context and cancellations skip reporting, plus browser-flow error reporting.
Sentry Error Reporter & App Integration
Sources/Auth/SentryAuthErrorReporter.swift, Sources/Auth/MacAuthComposition.swift, cmux.xcodeproj/project.pbxproj
SentryAuthErrorReporter implements AuthErrorReporting to capture errors in Sentry with telemetry opt-out respect, extracting error codes and populating scope tags/context; MacAuthComposition wires the reporter into AuthCoordinator; build files add source to compile phase.
Analytics Mapping Enhancement
Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SignInView.swift
signInFailureReason(_:) is extended to map distinct OAuth backend codes (INVALID_APPLE_CREDENTIALS, REDIRECT_URL_NOT_WHITELISTED, OAUTH_PROVIDER_ACCOUNT_ID_ALREADY_USED_FOR_SIGN_IN) to dedicated failure-reason strings instead of generic oauth_error.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes


Poem

🐰 Errors once silent, now they speak!
A seam is woven, structured and sleek.
OAuth and Sentry together unite,
Telemetry flows like rabbits take flight.
🌙✨

🚥 Pre-merge checks | ✅ 18 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (18 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly and specifically describes the main change: adding error capture for Sign in with Apple/OAuth failures instead of suppressing them.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed AuthErrorReporting protocol requires Sendable; implementations are stateless/implicitly Sendable; AuthErrorContext explicitly Sendable; errorReporter properly on @MainActor.
Cmux Swift Blocking Runtime ✅ Passed No blocking/timing primitives found. PR adds error-reporting seams using synchronous injection with @Sendable callbacks, no semaphores, locks, sleeps, asyncAfter, or main.sync in production code.
Cmux No Hacky Sleeps ✅ Passed PR contains only Swift files and Xcode project config; check scope is TypeScript/JavaScript/shell/non-Swift scripts. Swift timing covered by separate rule.
Cmux Algorithmic Complexity ✅ Passed PR adds error reporting with no algorithmic violations. Only collection operation is teams.contains(where:), a bounded one-time per-sign-in operation acceptable per rules.
Cmux Swift Concurrency ✅ Passed No legacy async patterns introduced. AuthErrorReporting is synchronous; SentrySDK closure is allowed third-party boundary. No DispatchQueue, Combine, or problematic Tasks.
Cmux Swift @Concurrent ✅ Passed AuthErrorReporting protocol is Sendable with synchronous report() method. SentrySDK.capture() handles async operations on background threads. No missing @concurrent or invalid annotations found.
Cmux Swift File And Package Boundaries ✅ Passed New files are small (14-34 lines) with single responsibilities. AuthCoordinator +30 lines under 250-line limit. No mixed UI/state/persistence/networking; package boundaries properly respected.
Cmux Swift Logging ✅ Passed No print/debugPrint/dump/NSLog violations in production code; AuthCoordinator uses proper os.log Logger; error context contains only non-sensitive metadata; telemetry respects opt-out.
Cmux User-Facing Error Privacy ✅ Passed Backend error codes mapped to localized user-friendly messages; analytics use generic enum strings; Sentry reporting respects telemetry opt-out. No raw codes exposed to users.
Cmux Full Internationalization ✅ Passed All user-facing strings use L10n.string() with localized entries for all supported locales (en, ja); infrastructure code has no user-facing text.
Cmux Swiftui State Layout ✅ Passed PR modifies only non-SwiftUI auth logic and diagnostics; only SwiftUI file touched (SignInView.swift) has documentation updates only, no new state patterns or layout changes.
Cmux Architecture Rethink ✅ Passed PR adds pure error-reporting injection seam with clear AuthCoordinator ownership; no timing repairs, duplicate state, split lifecycle, or observers introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR introduces error reporting infrastructure only; no NSWindow, NSPanel, NSWindowController, SwiftUI Window, or WindowGroup is created or modified, so the auxiliary window check is not applicable.
Cmux Source Artifacts ✅ Passed All changed files are hand-written source code, tests, and legitimate project configuration; no generated artifacts, logs, caches, or temp directories were added to source control.
Description check ✅ Passed The PR description is thorough and covers all required sections: it clearly explains why the change is needed, what was implemented, includes verification details, and provides helpful context about the underlying issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-siwa-error-capture

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.

@greptile-apps

greptile-apps Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an AuthErrorReporting seam to AuthCoordinator so that Sign in with Apple and other OAuth failures are captured by Sentry (macOS) and enriched in PostHog analytics (iOS) instead of being silently swallowed into the generic "Something went wrong" display message.

  • New Diagnostics/ types (AuthErrorReporting, AuthErrorContext, NoopAuthErrorReporting) are added to CmuxAuthRuntime; macOS injects SentryAuthErrorReporter at the composition root via MacAuthComposition, while iOS retains the no-op default.
  • signInWithOAuth and completeExternalSignIn now call errorReporter.report with the raw error and flow context before wrapping to a display-safe AuthError; user cancellation is explicitly excluded from reporting.
  • SignInView.signInFailureReason (iOS) adds distinct failure_reason codes for INVALID_APPLE_CREDENTIALS, REDIRECT_URL_NOT_WHITELISTED, and OAUTH_PROVIDER_ACCOUNT_ID_ALREADY_USED_FOR_SIGN_IN so these structural breaks are visible in analytics rather than collapsing to oauth_error.

Confidence Score: 4/5

Safe to merge — the change adds observability only and does not alter any auth flow control paths or user-visible behaviour.

All production auth paths are unchanged: the new errorReporter.report call is fire-and-forget before the existing throw, and the noop default means every existing call site is unaffected. The only concrete fixes left are two minor style issues (double AuthError(displaySafe:) evaluation) and a missing symmetry test for browser-flow cancellation suppression.

The two AuthCoordinator catch blocks have a minor double-evaluation pattern worth tidying, and AuthCoordinatorTests.swift is missing a completeExternalSignIn cancellation-suppression test.

Important Files Changed

Filename Overview
Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Coordinator/AuthCoordinator.swift Injects AuthErrorReporting seam and calls it in both OAuth and browser-flow catch blocks; AuthError(displaySafe:) is evaluated twice per catch block.
Sources/Auth/SentryAuthErrorReporter.swift Correctly wired at the macOS composition root; honors telemetry opt-out; no mutable state so Sendable is implicit and safe for @mainactor call sites.
Sources/Auth/MacAuthComposition.swift Minimal change; injects SentryAuthErrorReporter at construction site only.
Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SignInView.swift Adds three new failure_reason analytics codes (internal strings, not user-facing); user-facing error text was already localized via L10n.string in detailedErrorMessage.
Packages/CmuxAuthRuntime/Tests/CmuxAuthRuntimeTests/AuthCoordinatorTests.swift New tests cover OAuth failure reporting, OAuth cancellation suppression, and browser-flow failure reporting; cancellation-is-not-reported is only verified for native OAuth, not the browser path.

Sequence Diagram

sequenceDiagram
    participant UI
    participant AuthCoordinator
    participant AuthClient
    participant AuthErrorReporting
    participant AuthError

    UI->>AuthCoordinator: signInWithApple() / signInWithGoogle()
    AuthCoordinator->>AuthClient: signInWithOAuth(provider:anchor:)
    AuthClient-->>AuthCoordinator: throws OAuthError (e.g. INVALID_APPLE_CREDENTIALS)
    AuthCoordinator->>AuthError: displaySafe(error)
    alt "mapped != .cancelled"
        AuthCoordinator->>AuthErrorReporting: "report(error:, context: flow=oauth, provider=…)"
        Note over AuthErrorReporting: macOS: SentrySDK.capture()<br/>iOS/tests: NoopAuthErrorReporting
    end
    AuthCoordinator-->>UI: throw displayError ?? rawError

    UI->>AuthCoordinator: completeExternalSignIn() [macOS browser flow]
    AuthCoordinator->>AuthClient: currentUser(throwOnMissing:)
    AuthClient-->>AuthCoordinator: throws (session validation failure)
    AuthCoordinator->>AuthError: displaySafe(error)
    alt "mapped != .cancelled"
        AuthCoordinator->>AuthErrorReporting: "report(error:, context: flow=browser_oauth)"
    end
    AuthCoordinator-->>UI: throw displayError ?? rawError
Loading

Reviews (1): Last reviewed commit: "auth: also capture the macOS hosted-brow..." | Re-trigger Greptile

Comment on lines 411 to 424
} catch {
// Capture the real, un-wrapped error before it collapses into the
// generic display message, so OAuth/Apple sign-in breaks (e.g. a
// Stack INVALID_APPLE_CREDENTIALS from the native Apple token
// exchange) stop being invisible. User cancellation is not a
// failure, so it is not reported.
if AuthError(displaySafe: error) != .cancelled {
errorReporter.report(
error: error,
context: AuthErrorContext(flow: "oauth", provider: provider)
)
}
throw AuthError(displaySafe: error) ?? error
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 AuthError(displaySafe: error) is evaluated twice: once for the cancellation guard and again in the throw. If the mapping ever becomes non-trivial (or a future change makes it fallible in a different way) the two calls could diverge. A single binding makes the intent unambiguous and keeps the two uses in sync.

Suggested change
} catch {
// Capture the real, un-wrapped error before it collapses into the
// generic display message, so OAuth/Apple sign-in breaks (e.g. a
// Stack INVALID_APPLE_CREDENTIALS from the native Apple token
// exchange) stop being invisible. User cancellation is not a
// failure, so it is not reported.
if AuthError(displaySafe: error) != .cancelled {
errorReporter.report(
error: error,
context: AuthErrorContext(flow: "oauth", provider: provider)
)
}
throw AuthError(displaySafe: error) ?? error
}
} catch {
// Capture the real, un-wrapped error before it collapses into the
// generic display message, so OAuth/Apple sign-in breaks (e.g. a
// Stack INVALID_APPLE_CREDENTIALS from the native Apple token
// exchange) stop being invisible. User cancellation is not a
// failure, so it is not reported.
let displayError = AuthError(displaySafe: error)
if displayError != .cancelled {
errorReporter.report(
error: error,
context: AuthErrorContext(flow: "oauth", provider: provider)
)
}
throw displayError ?? error
}

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!

Comment on lines 447 to 460
} catch {
// The macOS hosted-browser flow (Sign in with Apple/Google via the
// web app) completes here, so capture its session-validation
// failures too; otherwise that primary macOS path stays invisible.
// The provider is not known at this seam (the browser ran the OAuth
// choice), so only the flow is reported.
if AuthError(displaySafe: error) != .cancelled {
errorReporter.report(
error: error,
context: AuthErrorContext(flow: "browser_oauth")
)
}
throw AuthError(displaySafe: error) ?? error
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Same double-evaluation pattern as in signInWithOAuth: AuthError(displaySafe: error) is called once for the cancellation check and again for the throw. Extracting to a let keeps both uses in sync.

Suggested change
} catch {
// The macOS hosted-browser flow (Sign in with Apple/Google via the
// web app) completes here, so capture its session-validation
// failures too; otherwise that primary macOS path stays invisible.
// The provider is not known at this seam (the browser ran the OAuth
// choice), so only the flow is reported.
if AuthError(displaySafe: error) != .cancelled {
errorReporter.report(
error: error,
context: AuthErrorContext(flow: "browser_oauth")
)
}
throw AuthError(displaySafe: error) ?? error
}
} catch {
// The macOS hosted-browser flow (Sign in with Apple/Google via the
// web app) completes here, so capture its session-validation
// failures too; otherwise that primary macOS path stays invisible.
// The provider is not known at this seam (the browser ran the OAuth
// choice), so only the flow is reported.
let displayError = AuthError(displaySafe: error)
if displayError != .cancelled {
errorReporter.report(
error: error,
context: AuthErrorContext(flow: "browser_oauth")
)
}
throw displayError ?? error
}

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!

Comment on lines 353 to +372
#expect(coordinator.isAuthenticated == false)
#expect(coordinator.isLoading == false)
}

@Test func browserFlowCompletionFailureIsReported() async {
// The macOS hosted-browser sign-in (Apple/Google via the web app)
// completes through completeExternalSignIn(); a validation failure
// there must reach the reporter so the primary macOS path is visible.
let client = FakeAuthClient()
await client.setThrowOnCurrentUser(AuthError.serverError(500, "auth_failed"))
let reporter = FakeAuthErrorReporter()
let (coordinator, _) = makeCoordinator(client: client, errorReporter: reporter)

await #expect(throws: (any Error).self) {
try await coordinator.completeExternalSignIn()
}

#expect(reporter.reports.count == 1)
#expect(reporter.reports.first?.context.flow == "browser_oauth")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Missing cancellation-suppression test for the browser flow. oauthCancellationIsNotReported verifies that AuthError.cancelled from native OAuth doesn't reach the reporter, but there's no symmetric test for completeExternalSignIn. The production guard is identical in both paths, so a future refactor that accidentally drops it from completeExternalSignIn would go undetected. A browserFlowCancellationIsNotReported test covering the same scenario via setThrowOnCurrentUser(AuthError.cancelled) would close the gap.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Packages/CmuxAuthRuntime/Tests/CmuxAuthRuntimeTests/AuthCoordinatorTests.swift`:
- Around line 98-101: Update the tests that inspect reporter.reports to also
assert the underlying error contract: after the existing checks on
reporter.reports.count and reports.first?.context.flow/provider, add an
assertion that reports.first?.error is the backend error with the expected
Stack/Auth error code (check the error's code/enum value used in your auth
stack), and in the browser-flow test explicitly assert
reports.first?.context.provider == nil (since provider is intentionally unknown
there); locate these changes around the tests referencing reporter.reports and
reports.first? to add the error-code and nil-provider assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 28a6c2d6-0fe1-4633-bae2-f1c35b9233b7

📥 Commits

Reviewing files that changed from the base of the PR and between 8d0ed95 and ed0177a.

📒 Files selected for processing (10)
  • Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Coordinator/AuthCoordinator.swift
  • Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Diagnostics/AuthErrorContext.swift
  • Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Diagnostics/AuthErrorReporting.swift
  • Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Diagnostics/NoopAuthErrorReporting.swift
  • Packages/CmuxAuthRuntime/Tests/CmuxAuthRuntimeTests/AuthCoordinatorTests.swift
  • Packages/CmuxAuthRuntime/Tests/CmuxAuthRuntimeTests/Fakes.swift
  • Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SignInView.swift
  • Sources/Auth/MacAuthComposition.swift
  • Sources/Auth/SentryAuthErrorReporter.swift
  • cmux.xcodeproj/project.pbxproj

Comment on lines +98 to +101
#expect(reporter.reports.count == 1)
#expect(reporter.reports.first?.context.flow == "oauth")
#expect(reporter.reports.first?.context.provider == "apple")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Strengthen assertions to lock the “underlying error” contract.

These tests currently validate only flow/provider metadata; they would still pass if the reporter started receiving a wrapped display-safe error instead of the original backend error. Please also assert the captured reports.first?.error carries the expected Stack/Auth code, and in the browser-flow test assert context.provider == nil (provider is intentionally unknown at that seam).

Suggested assertion additions
@@
         `#expect`(reporter.reports.count == 1)
         `#expect`(reporter.reports.first?.context.flow == "oauth")
         `#expect`(reporter.reports.first?.context.provider == "apple")
+        let oauthError = reporter.reports.first?.error as? AuthError
+        `#expect`(oauthError == AuthError.serverError(400, "INVALID_APPLE_CREDENTIALS"))
@@
         `#expect`(reporter.reports.count == 1)
         `#expect`(reporter.reports.first?.context.flow == "browser_oauth")
+        `#expect`(reporter.reports.first?.context.provider == nil)
+        let browserError = reporter.reports.first?.error as? AuthError
+        `#expect`(browserError == AuthError.serverError(500, "auth_failed"))

Also applies to: 370-372

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/CmuxAuthRuntime/Tests/CmuxAuthRuntimeTests/AuthCoordinatorTests.swift`
around lines 98 - 101, Update the tests that inspect reporter.reports to also
assert the underlying error contract: after the existing checks on
reporter.reports.count and reports.first?.context.flow/provider, add an
assertion that reports.first?.error is the backend error with the expected
Stack/Auth error code (check the error's code/enum value used in your auth
stack), and in the browser-flow test explicitly assert
reports.first?.context.provider == nil (since provider is intentionally unknown
there); locate these changes around the tests referencing reporter.reports and
reports.first? to add the error-code and nil-provider assertions.

This branch was successfully deployed

1 active deployment
Preview – cmux — ed0177a0 Deployed Jun 8, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants