Backward

fetch() and the missing Cookie on React Native Android



Strange things happen on React Native Android, during our recent development for MockingBot Android.

If you just complete a fetch() for user Auth, and then close the App manually (and the background process) in 10 seconds. Most of the time your Cookie will not be saved to the Storage. So next time you open your App, you need to do the Auth process again. (or worse, an Error throws)

Explain, explain! (in Dalek voice)

Dive into the source code of React Native, there’s something interesting.

Let me explain this in a right order: (I assume)

Since Android WebView is used in React Native Android as the Component, it’s natural to use the provided CookieManager and CookieSyncManager(deprecated in API21) to handle Cookies.

Quote from CookieSyncManager

Synchronize the browser cookie store between RAM and permanent storage. 
To get the best performance, browser cookies are saved in RAM. 
A separate thread saves the cookies between, driven by a timer. 
… 
The sync interval is 5 minutes, so you will want to force syncs manually anyway.

React Native kept this strategy in ForwardingCookieHandler, but reduced the sync interval down to 30 seconds.

So if the App is closed fast enough (like in 10 seconds), Cookie may be lost.

Exterminate, exterminate! (in Dalek voice)

To solve this edge case, you can either:

  • Ask user to keep the App in memory for 30 seconds, after each network request.
  • Or add this code to MainActivity.java as fail-safe:
import android.webkit.CookieManager;
...
public class MainActivity extends ReactActivity {
    ...      
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initCookie();
    }
    @Override
    protected void onStop() {
        saveCookie();
        super.onStop();
    }
    private void initCookie() {
        if (Build.VERSION.SDK_INT < 21) CookieSyncManager.createInstance(this);
    }
    private void saveCookie() {
        if (Build.VERSION.SDK_INT >= 21) CookieManager.getInstance().flush();
        else CookieSyncManager.getInstance().sync();
    }
}

Extend: about fetch()

Miss writing AJAX with XMLHttpRequest? The newer Fetch API is supported (directly or via package) in most browser, node.

Using fetch() is simple:

fetch('https://some-site.com/some-data.json')
  .then((response) => response.json())
  .then((response) => console.log(response))

And it’s also available in React Native. (HTTPS only, though) React Native correctly handles Cookie for you, with the option set:

fetch('https://some-site.com/some-data.json', { credentials: 'same-origin' })

Cookie will be sent with the network request. Detail on credentials.

This allows for implementing Apps with traditional Cookie-based Auth in React Native:

  • First require Auth with a request like:
fetch('https://some-site.com/auth', {
  method: 'POST',
  credentials: 'same-origin',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: `email=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}`
})

  • Then make follow up data requests, like:
fetch('https://some-site.com/userdata.json', {
  method: 'GET',
  credentials: 'same-origin'
})

Except the Cookie is not visible to JavaScript, this is not much different from what we do in browser.

Comments 0