Skip to content

[BUG] iOS: API key auth fails for custom OpenAI-compatible endpoints #3516

Description

@wjf3121

Bug Description
On iOS, custom OpenAI-compatible endpoints with API key authentication fail to work. The same configuration (custom base URL + API key) works correctly on desktop. Disabling authentication on the server side makes it work on iOS.

Steps to Reproduce

  1. Open Chatbox on iOS
  2. Configure a custom OpenAI-compatible provider with a custom endpoint URL and API key (e.g., LM Studio with auth enabled)
  3. Send a message
  4. Request fails

Expected Results
The request should succeed and return a chat completion, the same as on desktop.

Actual Results
The request fails on iOS. The server (e.g., LM Studio) logs: "Unexpected endpoint or method. (POST /v1/chat/completions). Returning 200 anyway". Disabling auth on the server makes it work on iOS.

Screenshots
N/A

Desktop (please complete the following information):

  • Operating System: iOS 26.3
  • Application Version: Chatbox v1.19.0

Additional Context

Root Cause Analysis (suggested by Claude, not verified manually)

In src/renderer/utils/request.ts, doRequest only routes mobile requests through handleMobileRequest (which uses CapacitorHttp / native HTTP) when useProxy is true:

// request.ts line 64-66
if (platform.type === 'mobile' && useProxy) {  // BUG: requires useProxy to be true
    return handleMobileRequest(requestUrl, method, headers, body, signal)
}
// Falls through to plain fetch() in WKWebView
const res = await fetch(requestUrl, { method, headers, body, signal })

Why this fails on iOS but works on desktop:

  • Desktop (Electron): webSecurity: false is set in src/main/main.ts:266, disabling CORS enforcement entirely. Plain fetch() works without restrictions.
  • iOS (Capacitor/WKWebView): CORS is enforced. The Authorization: Bearer <key> header is a non-simple header that triggers a CORS preflight (OPTIONS request). Servers like LM Studio don't handle the OPTIONS preflight properly, causing the request to fail or lose its auth header.

Why disabling auth fixes it: Without the Authorization header, no CORS preflight is triggered and the request goes through as a "simple request".

Why CapacitorHttp fixes it: CapacitorHttp makes native HTTP calls via iOS URLSession, bypassing WKWebView's CORS enforcement entirely — no preflight, no header restrictions.

For custom OpenAI-compatible endpoints, useProxy is typically false/undefined, so on iOS the request falls through to plain fetch() instead of CapacitorHttp.

Suggested Fix (suggested by Claude, not verified manually)

On mobile, always route through handleMobileRequest regardless of useProxy:

// Change from:
if (platform.type === 'mobile' && useProxy) {

// To:
if (platform.type === 'mobile') {

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions