skip to content

Why is cookie-based session authentication with fetch fragile in a React Native app, and what do most teams use instead?

level: seniorimportance: should knowfreq 30%

answer

  1. cookies live outside JavaScript
  2. one native cookie jar per app
  3. withCredentials defaults to true
  4. documented fetch cookie known issues
  5. token in a header, refresh token secured

basics

~20 s

React Native's fetch stores cookies in the platform's shared native cookie store, outside JavaScript's control; the docs list credentials 'omit', redirect 'manual' and cookies set on iOS 302 redirects as unreliable. Most apps send a bearer token in the Authorization header instead.

solid answer

~50 s

In React Native the cookie jar is native: on iOS the networking layer uses `NSHTTPCookieStorage`'s shared storage, and on Android cookies are forwarded to the same `CookieManager` that WebViews use. JavaScript cannot see or scope that jar per request, and React Native's `XMLHttpRequest` defaults `withCredentials` to `true`, so stored cookies ride along on every request to that host. The React Native networking docs list known issues: `credentials: 'omit'` and `redirect: 'manual'` do not work with the built-in `fetch`, cookie auth is described as unstable, and on iOS a `Set-Cookie` on a `302` redirect may not be stored, which can loop on an expired session. So most apps authenticate with a short-lived access token in an `Authorization: Bearer` header, keep the refresh token in secure storage, and clear the native jar on logout if any cookies exist.

go deeper

for a junior

Recall that React Native keeps cookies in a native store you do not control from JavaScript, and that most apps send a bearer token in a header instead.

for a middle

Explain the mechanics: shared native jar on each platform, withCredentials defaulting to true, and the documented fetch issues with credentials 'omit' and manual redirects.

for a senior

Show the production judgment: design token auth with a refresh flow, clear the native jar on logout, and anticipate WebView cookie sharing and iOS redirect loops.

for a principal

Weigh keeping a cookie-session backend for web and mobile against adding a token endpoint for native clients, including revocation, CSRF and one auth model across platforms.

## Where cookies live in React Native In a browser, cookies belong to the browser profile and `fetch`'s `credentials` option controls whether they are sent. In React Native there is no browser, so cookies are handled by the **native networking stack**: - **iOS** – React Native's HTTP handler configures its `NSURLSession` to accept all cookies and store them in `NSHTTPCookieStorage`'s **shared storage**, which persists across launches. - **Android** – React Native's OkHttp client uses a cookie handler that forwards cookies to the WebView **`CookieManager`**, so app requests and WebViews share one jar. - **Expo SDK 56+** – the global `fetch` is `expo/fetch`; it uses the same shared stores on both platforms and defaults `credentials` to `'include'`. JavaScript never holds the cookies. It cannot list them, scope them per request, or reliably tell whether a request carried one. ## Why that makes session cookies fragile 1. **Always-on credentials.** React Native's `XMLHttpRequest` initialises `withCredentials` to `true` (the browser default is `false`), and the built-in `fetch` sits on top of it. Stored cookies for a host go out on every request. 2. **Documented known issues.** The React Native networking guide lists `credentials: 'omit'` and `redirect: 'manual'` as not working with `fetch`, calls cookie-based authentication unstable, and notes that on iOS a `Set-Cookie` header on a `302` redirect may not be set. Because the redirect cannot be handled manually, an expired session that redirects to login can turn into repeated requests. 3. **Platform differences.** The same guide notes that on Android, same-name headers leave only the last one present, so anything relying on repeated headers behaves differently per platform. 4. **Lifecycle surprises.** The jar outlives the JavaScript state. A user who logs out in JavaScript can still send the old session cookie unless the native store is cleared, and a WebView login on Android silently shares cookies with `fetch`. ## What teams use instead | Concern | Cookie session | Token in a header | |---|---|---| | Who controls sending | Native jar, always on | Your code, per request | | Visible to JavaScript | No | Yes (you set the header) | | Logout | Clear the native jar | Drop tokens, revoke refresh token | | Works the same on iOS and Android | Not reliably | Yes | | CSRF exposure | Needs thought | Not applicable to header tokens | The usual design: - **Access token** – short-lived, kept in memory, attached by one request helper or an axios interceptor as `Authorization: Bearer <token>`. - **Refresh token** – longer-lived, stored in the platform keychain or keystore through a secure-storage library, never in plain key-value storage. - **Refresh flow** – on a `401`, refresh once, retry the original request, and log out if the refresh fails. If a backend genuinely requires cookies (a legacy session or a hosted login shared with a WebView), keep the cookie traffic to that host, test redirects on both platforms, and clear the jar on logout. React Native's `Networking` export has a `clearCookies(callback)` method that empties the native store the networking layer uses. ## Getting logout right Whichever scheme you use, logout has to clean up every layer, in order: 1. **Tell the server** to revoke the refresh token or end the session, so a copied credential stops working. 2. **Delete the tokens** from memory and from secure storage. 3. **Clear the native cookie store** if the app ever talked to a cookie-setting host, including hosts visited in a WebView. 4. **Reset client caches** that hold user data, so the next account does not see the previous one's screens. Skipping step 3 is the classic React Native bug: the JavaScript state says "logged out" while the native jar still carries a valid session cookie, so the next request, possibly made for a different user, is authenticated as the old one. ## What to say in an interview Name the mechanism (**a shared native cookie store outside JavaScript**), cite the concrete failure modes (**`credentials: 'omit'` ignored, iOS `302` `Set-Cookie`, credentials on by default**), and give the replacement (**header tokens plus a securely stored refresh token**). Mentioning that Android shares the jar with WebViews shows you have debugged a real login flow.

  • A user logs out, you clear your auth state in JavaScript, yet the next request is still authenticated. Why?
    The server session cookie is still in the native cookie store, which JavaScript state does not control, and React Native sends stored cookies by default. Clear the native jar on logout, for example with the `clearCookies` method on React Native's `Networking` export, and invalidate the session server-side.
  • Why can a login done in a WebView on Android make later fetch calls authenticated?
    React Native's Android networking forwards cookies to the WebView `CookieManager`, so both share one jar. A session cookie set during the WebView login is sent by later `fetch` calls to the same host, which can be useful but is easy to leak across accounts if you never clear it.
  • Where do you attach the access token so no screen forgets it?
    In one request layer: a small `fetch` wrapper or an axios request interceptor that reads the in-memory access token and sets `Authorization: Bearer ...`. The same layer handles a `401` by refreshing once and retrying, so screens never deal with tokens.

saying these in an interview costs you the question

  • Setting credentials: 'omit' reliably stops React Native's fetch from sending cookies.
  • React Native's XMLHttpRequest defaults withCredentials to false, like browsers.
  • Clearing the auth state in JavaScript also removes the session cookie.
  • Cookies are kept in AsyncStorage by React Native's networking layer.
  • iOS and Android handle cookies on redirects in exactly the same way.