Home > Mobile >  React Native - Webview set cookies policy
React Native - Webview set cookies policy

Time:10-07

To give you context, our app is a react-native web wrapper (something similar to what Cordova or ionic does).

The app downloads an updated version of the web bundle that is stored locally after the user opens the app, and then the WebView component uses it as a source to show the app to the user.

There are some parts of this web app that uses web sockets, so the issue is that considering its large traffic, the socket/exchange service has a bunch of nodes/replicas and in order to keep connections alive the load balancer uses Set-Cookie headers to make sure that the client is going to the right service node in every request

So, what we're trying is:

  • Enable Set-Cookie behavior within a WebView
  • Get Set-Cookie response header manually from a request that is happening within a WebView

Our react-native application looks like:

import React from 'react';
import {Platform, StyleSheet, View} from 'react-native';
import {WebView} from 'react-native-webview';

type WrapperProps = {
  path: string;
};

function Wrapper({path}: WrapperProps) {
  return (
    <View style={styles.container}>
      <WebView
        allowFileAccess
        allowUniversalAccessFromFileURLs
        allowingReadAccessToURL={path}
        javaScriptEnabled
        originWhitelist={['*']}
        sharedCookiesEnabled
        source={{
          uri:
            Platform.OS === 'ios'
              ? `file://${path}/index.html`
              : `${path}/index.html`,
        }}
        injectedJavaScript={`(function () {
          window.ReactNativeWebView.postMessage(JSON.stringify(document.coookie));
          var open = XMLHttpRequest.prototype.open;
          XMLHttpRequest.prototype.open = function (method, url) {
            this.addEventListener("load", function () {
              var responseHeaders = this.getAllResponseHeaders();
              var setCookies = this.getResponseHeader("Set-Cookie"); // always return null
              window.ReactNativeWebView.postMessage(JSON.stringify({ setCookies }));
              window.ReactNativeWebView.postMessage(
                JSON.stringify({ responseHeaders }) // return all headers except for Set-Cookie 
              );
            });
            open.apply(this, arguments);
          };
        })();
        `}
        onMessage={e => {
          console.log({onMessageEvent: e.nativeEvent.data});
        }}
        style={styles.webview}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  webview: {
    height: '100%',
    width: '100%',
  },
});

export default React.memo(Wrapper);

Do you have any clue how to solve this issue?

P.S: We already read How to set WKWebView cookie to accept Policy - But it's for swift.

CodePudding user response:

I managed to

Enable Set-Cookie behavior within a WebView

by establishing a socket connection using socket.io-client, so the web socket requests within the WebView will already include cookies in request headers (which will keep connections alive).

Considering react-native-webview uses WKWebView on iOS, there is no way to access cookies in response headers. However, in this particular scenario you can create the socket connection from outside the web view, handle cookies (it seems socket.io-client already does), and then they should be available in the requests within the WebView.

In order to setup socket io in your react-native app, you will need to

  • Install [email protected] (I tried different versions but this was the only one that worked)
  • Assign window.navigator.userAgent = 'react-native'; (since ES6 modules are hoisted, this assignment must not be done in the same file as react-native and socket io imports)
  • Establish your socket connection.

So it should look like:

// userAgent.js
window.navigator.userAgent = 'react-native';

// Wrapper.tsx
import React from 'react';
import {Platform, StyleSheet, View} from 'react-native';
import {WebView} from 'react-native-webview';
// user agent MUST be imported before socket io
import './userAgent';
import io from 'socket.io-client';


export const socket = io('https://your.host.com/', {
  path: '/path/to/socket.io/',
  jsonp: false,
});


type WrapperProps = {
  path: string;
};

function Wrapper({path}: WrapperProps) {
  return (
    <View style={styles.container}>
      <WebView
        allowFileAccess
        allowUniversalAccessFromFileURLs
        allowingReadAccessToURL={path}
        javaScriptEnabled
        originWhitelist={['*']}
        sharedCookiesEnabled
        source={{
          uri:
            Platform.OS === 'ios'
              ? `file://${path}/index.html`
              : `${path}/index.html`,
        }}
        style={styles.webview}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  webview: {
    height: '100%',
    width: '100%',
  },
});

export default React.memo(Wrapper);
  • Related