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
- 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).
- Open an
EventSource to that host with withCredentials: false (or omitted) and a manual Cookie header in options.headers.
- 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
withCredentials: falseis a no-op on React Native — cookies are always sent (RN's XHR defaultswithCredentialstotrue)Summary
EventSource'swithCredentialsoption defaults tofalse, but passingfalse(or relying on the default) does not disable credentials. The constructor only assigns the underlying XHR property when the option is truthy:When the option is
false, the XHR is left untouched — and React Native'sXMLHttpRequestdefaultswithCredentialstotrue, the opposite of the WHATWG default: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: falsesilently does nothing.Impact
Any app that sets auth headers manually (e.g. a
CookieorAuthorizationheader viaoptions.headers) gets the native cookie jar's cookies layered on top:iOS:
RCTNetworkingsets the request'sCookieheader fromNSHTTPCookieStorage(HTTPShouldHandleCookies = withCredentials), then merges supplied headers withaddValue:forHTTPHeaderField:— which comma-joins onto the existing field. A manually-suppliedCookie: session=Xbecomes:That's malformed per RFC 6265 (cookie pairs must be
;-separated, andCookiemust be a single header); real-world server frameworks (e.g. Spring) fail to parse the value and auth breaks.Android:
withCredentialsis forwarded toNetworkingModule, which only detaches the OkHttp cookie jar when it'sfalse(clientBuilder.cookieJar(CookieJar.NO_COOKIES)) — which, per the above, can never happen through this library.Reproduction
Set-Cookieon a previous request).EventSourceto that host withwithCredentials: false(or omitted) and a manualCookieheader inoptions.headers.Cookieheader 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
falsereaches the XHR and overrides RN's non-spec default: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 = NOon iOS).One consideration: apps that unknowingly relied on jar cookies being sent by default would see them stop when they pass
withCredentials: falseexplicitly — but that's the documented meaning of the option. Keeping the default attrueon RN (matching RN's XHR) while honoring an explicitfalsewould also work if you prefer strict back-compat:Environment
XMLHttpRequest.js/RCTNetworking.mm/NetworkingModule.ktsources; the relevant RN behavior is long-standing)