auth: send Hexclave header names to Stack; show sign-in error details

Merged
LA
25d ago
+4095-3762

Summary

Native sign-in fails most of the time with "cmux sign-in is temporarily unavailable". Since about 06:30 UTC on 2026-10-13, Stack answers 429 with an HTML body to most requests that carry the legacy x-stack-* request headers (#20236 found the same thing for the web server). The vendored Swift SDK sends only those names. So after the browser callback brings tokens back, users/me gets 429. The SDK maps the 429 to RATE_LIMITED, and the app shows the generic server-error screen. Token refresh (/auth/oauth/token) sends the same headers.

This PR:

  • Vendored SDK: sends every request header to Stack by its x-hexclave-* name. It reads response headers by the Hexclave name first, then the legacy name. A 429 or other HTTP failure now carries the HTTP status and the Stack request ID.
  • Sign-in failure screen: shows a token-free detail line (error code, HTTP status, request ID), for example RATE_LIMITED · HTTP 429 · Request ID 3mxpbb4fcbydy3gz. The line is selectable, and a Copy Error Details button copies it. Users can now report the exact failure.

Evidence

Production probe of GET https://api.stack-auth.com/api/v1/users/me with the public production project ID and publishable key and an invalid access token. Requests were sent one at a time, 3 s apart:

Roundx-stack-* headers (what the app sends)x-hexclave-* headers
1429 (HTML)401 UNPARSABLE_ACCESS_TOKEN
2401401
3429401
4429401

Stack accepts x-hexclave-override-error-status, x-hexclave-random-nonce, x-hexclave-client-version and x-hexclave-refresh-token. With those headers it answers 200 with x-hexclave-actual-status: 401, x-hexclave-known-error and x-hexclave-request-id. With an invalid refresh token, POST /auth/sessions/current/refresh with Hexclave headers answers 401 REFRESH_TOKEN_NOT_FOUND_OR_EXPIRED, not 429.

Reported on cmux RC 0.65.2-rc (rc/v0.65.2). The native auth code is the same in 0.65.1, so stable and iOS are affected too.

Verification

  • python3 scripts/verify-local.py --only swift-syntax --swift-changed: passed.
  • ./scripts/localize-changes and python3 scripts/localization_catalog.py check: 0 parity errors. The 2 new keys have all 9 locales.
  • Fleet build of this SHA: pending; see the comment below.
  • Not yet done: an end-to-end sign-in in a tagged build with the fix.

Changelog

Fixed: Signing in to cmux no longer fails with "cmux sign-in is temporarily unavailable"; sign-in errors now show copyable details.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes native sign-in failing with "cmux sign-in is temporarily unavailable". Since 2026-10-13, Stack returns 429 with an HTML body to most requests carrying the legacy x-stack-* headers, so the vendored SDK now sends x-hexclave-* header names. The sign-in error screen also shows a copyable detail line (error code, HTTP status, request ID) instead of only the generic server-error message.

  • Vendored SDK: request headers use the x-hexclave-* names; response headers are read by the Hexclave name first, then the legacy name. HTTP failures and known Stack errors now include the status and Stack request ID.
  • Sign-in failure screen: shows a token-free, selectable detail line with a Copy Error Details button. The detail comes from the raw error before it collapses to a display-safe error, and the "Copied" state belongs to the exact detail shown.
  • New and updated copy has all 9 locales; no localization parity errors.

Summary by CodeRabbit

  • New Features
    • Sign-in failures now display diagnostic details that can be selected and copied.
    • Copy controls indicate when details have been copied and update when the failure details change.
    • Diagnostic details can include error codes, HTTP status, and request ID when available.
    • Added localized messages for sign-in diagnostics and other interface areas, including settings, notifications, and Cloud.
  • Bug Fixes
    • Improved diagnostic details for failed external sign-in attempts.