Skip to content

withCredentials: false is a no-op on React Native #84

Description

@re5et

withCredentials: false is a no-op on React Native — cookies are always sent (RN's XHR defaults withCredentials to true)

Summary

EventSource's withCredentials option defaults to false, but passing false (or relying on the default) does not disable credentials. The constructor only assigns the underlying XHR property when the option is truthy:

// src/EventSource.js
this.withCredentials = options.withCredentials || false;
...
if (this.withCredentials) {
  this._xhr.withCredentials = true;
}

When the option is false, the XHR is left untouched — and React Native's XMLHttpRequest defaults withCredentials to true, the opposite of the WHATWG default:

// react-native/Libraries/Network/XMLHttpRequest.js
withCredentials: boolean = true;

So the effective behavior is: cookies from the native jar are always attached, and there is no way to turn them off through this library's API. withCredentials: false silently does nothing.

Impact

Any app that sets auth headers manually (e.g. a Cookie or Authorization header via options.headers) gets the native cookie jar's cookies layered on top:

  • iOS: RCTNetworking sets the request's Cookie header from NSHTTPCookieStorage (HTTPShouldHandleCookies = withCredentials), then merges supplied headers with addValue:forHTTPHeaderField: — which comma-joins onto the existing field. A manually-supplied Cookie: session=X becomes:

    Cookie: session=X,session=X
    

    That's malformed per RFC 6265 (cookie pairs must be ; -separated, and Cookie must be a single header); real-world server frameworks (e.g. Spring) fail to parse the value and auth breaks.

  • Android: withCredentials is forwarded to NetworkingModule, which only detaches the OkHttp cookie jar when it's false (clientBuilder.cookieJar(CookieJar.NO_COOKIES)) — which, per the above, can never happen through this library.

Reproduction

  1. In a React Native app, have any cookie for your API host in the native jar (e.g. from a prior webview login or a Set-Cookie on a previous request).
  2. Open an EventSource to that host with withCredentials: false (or omitted) and a manual Cookie header in options.headers.
  3. Inspect the outgoing request (proxy/Charles): the jar cookie is present; with a manual Cookie header it's comma-folded into one malformed value on iOS.

Expected

withCredentials: false (the documented default) disables credentials, matching the EventSource/XHR spec semantics the option name implies.

Suggested fix

Assign the value through unconditionally, so false reaches the XHR and overrides RN's non-spec default:

-      if (this.withCredentials) {
-        this._xhr.withCredentials = true;
-      }
+      this._xhr.withCredentials = this.withCredentials;

This is what we're running in production via patch-package, and it behaves as expected on both platforms (jar detached on Android via CookieJar.NO_COOKIES, HTTPShouldHandleCookies = NO on iOS).

One consideration: apps that unknowingly relied on jar cookies being sent by default would see them stop when they pass withCredentials: false explicitly — but that's the documented meaning of the option. Keeping the default at true on RN (matching RN's XHR) while honoring an explicit false would also work if you prefer strict back-compat:

this.withCredentials = options.withCredentials ?? true; // document RN's actual default
...
this._xhr.withCredentials = this.withCredentials;

Environment

  • react-native-sse 1.2.1
  • react-native 0.86.0 (verified against its XMLHttpRequest.js / RCTNetworking.mm / NetworkingModule.kt sources; the relevant RN behavior is long-standing)
  • iOS + Android

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions