12

I am working on one app, In which I need to keep users last web-session active in react-native-webview.

Here is a required work-flow.

  • My app is only have one WebView, Where there is fixed url is loading.

  • User will open app & login to that website.

  • When user will kill app & open it again, I need to keep that user logged in as he has already logged in last time.

Here is what i tried so far:

// list cookies (IOS ONLY)

CookieManager.getAll(useWebKit)
    .then((res) => {
        console.log('CookieManager.getAll from webkit-view =>', res);
    });

But as it's suggested, It'll only work in iOS. I am also not able to set that cookies in website that is opened in WebView to keep that session active.

But I didn't found any success yet.

Any suggestion or solution will highly appreciated.

======= Update =======

For Android:

It is working by default, That means we only need to check this for iOS.

Vishal Sharma
  • 1,733
  • 2
  • 12
  • 32
  • from my experience, this seems relevant only to session cookies (which don't have an expiry date in the future). – Roey May 27 '21 at 10:19

1 Answers1

9

actually this problem exist in react-native-webview

Cookies are lost on iOS after application closing

it should be resolve through native code. but today I made a solution and tested with PHP website

Full Code

import React, {Component} from 'react';
import {StyleSheet, SafeAreaView} from 'react-native';
import {WebView} from 'react-native-webview';
import CookieManager from '@react-native-community/cookies';
import AsyncStorage from '@react-native-community/async-storage';

let domain="http://example.com";

export default class App extends Component {
  constructor(props) {
    super(props);
    this.currentUrl = '';
    this.myWebView = React.createRef();
    this.state = {
      isReady: false,
      cookiesString: '',
    };
  }

  jsonCookiesToCookieString = (json) => {
    let cookiesString = '';
    for (let [key, value] of Object.entries(json)) {
      cookiesString += `${key}=${value.value}; `;
    }
    return cookiesString;
  };

  componentWillMount() {
    this.provideMeSavedCookies()
      .then(async (savedCookies) => {
        let cookiesString = this.jsonCookiesToCookieString(savedCookies);
        const PHPSESSID = await AsyncStorage.getItem('PHPSESSID');
        if (PHPSESSID) {
          cookiesString += `PHPSESSID=${PHPSESSID};`;
        }
        this.setState({cookiesString, isReady: true});
      })
      .catch((e) => {
        this.setState({isReady: true});
      });
  }

  onLoadEnd = (syntheticEvent) => {
    let successUrl = `${domain}/report.php`;
    if (this.currentUrl === successUrl) {
      CookieManager.getAll(true).then((res) => {
        AsyncStorage.setItem('savedCookies', JSON.stringify(res));
        if (res.PHPSESSID) {
          AsyncStorage.setItem('PHPSESSID', res.PHPSESSID.value);
        }
      });
    }
  };
  onNavigationStateChange = (navState) => {
    this.currentUrl = navState.url;
  };


  provideMeSavedCookies = async () => {
    try {
      let value = await AsyncStorage.getItem('savedCookies');
      if (value !== null) {
        return Promise.resolve(JSON.parse(value));
      }
    } catch (error) {
      return {}
    }
  };

  render() {
    const {cookiesString, isReady} = this.state;
    if (!isReady) {
      return null;
    }
    return (
      <SafeAreaView style={styles.container}>
        <WebView
          ref={this.myWebView}
          source={{
            uri: `${domain}`,
            headers: {
              Cookie: cookiesString,
            },
          }}
          scalesPageToFit
          useWebKit
          onLoadEnd={this.onLoadEnd}
          onNavigationStateChange={this.onNavigationStateChange}
          sharedCookiesEnabled
          javaScriptEnabled={true}
          domStorageEnabled={true}
          style={styles.WebViewStyle}
        />
      </SafeAreaView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#FFF',
  },
  WebViewStyle: {
    flex: 1,
    resizeMode: 'cover',
  },
});

Detail:

Step 1:

get cookies after login and save in AsyncStorage like this

onNavigationStateChange = (navState) => {
    this.currentUrl = navState.url;
  };
 onLoadEnd = () => {
    let successUrl = `${domain}/report.php`;
    if (this.currentUrl === successUrl) {
      CookieManager.getAll(true).then((res) => {
        AsyncStorage.setItem('savedCookies', JSON.stringify(res));
        if (res.PHPSESSID) {
          AsyncStorage.setItem('PHPSESSID', res.PHPSESSID.value);
        }
      });
    }
  };

Step 2:

enable sharedCookiesEnabled props and get saved cookies in componentWillMount and make required webview header cookies formate by jsonCookiesToCookieString function and stop render webview by isReady props utils you get cookies

jsonCookiesToCookieString = (json) => {
    let cookiesString = '';
    for (let [key, value] of Object.entries(json)) {
      cookiesString += `${key}=${value.value}; `;
    }
    return cookiesString;
  };
provideMeSavedCookies = async () => {
    try {
      let value = await AsyncStorage.getItem('savedCookies');
      if (value !== null) {
        return Promise.resolve(JSON.parse(value));
      }
    } catch (error) {
      return {}
    }
  };
componentWillMount() {
    this.provideMeSavedCookies()
      .then(async (savedCookies) => {
        let cookiesString = this.jsonCookiesToCookieString(savedCookies);
        const PHPSESSID = await AsyncStorage.getItem('PHPSESSID');
        if (PHPSESSID) {
          cookiesString += `PHPSESSID=${PHPSESSID};`;
        }
        this.setState({cookiesString, isReady: true});
      })
      .catch((e) => {
        this.setState({isReady: true});
      });
  }

Step 3:

pass cookiesString in Webview Header and write render function like this

render() {
    const {cookiesString, isReady} = this.state;
    if (!isReady) {
      return null;
    }
    return (
      <SafeAreaView style={styles.container}>
        <WebView
          ref={this.myWebView}
          source={{
            uri: `${domain}`,
            headers: {
              Cookie: cookiesString,
            },
          }}
          scalesPageToFit
          useWebKit
          onLoadEnd={this.onLoadEnd}
          onNavigationStateChange={this.onNavigationStateChange}
          sharedCookiesEnabled
          javaScriptEnabled={true}
          domStorageEnabled={true}
          style={styles.WebViewStyle}
        />
      </SafeAreaView>
    );
  }
Muhammad Numan
  • 23,222
  • 6
  • 63
  • 80
  • Hi @Muhammad Numan, It is already enabled, But website shows login page always rather then DashBoard. – Vishal Sharma May 29 '20 at 03:51
  • can you share website link and webview component in render so I can identify your problem – Muhammad Numan May 29 '20 at 04:44
  • Hi, can you share your contact details ? Cause i need to share you demo login credentials – Vishal Sharma May 29 '20 at 05:15
  • 2
    Note: This solution does not work with http-only cookie (obviously) – Kazuya Gosho Dec 05 '22 at 10:03
  • @KazuyaGosho do you have solution to make it work with http-only? – Prabhakaran Aug 10 '23 at 10:26
  • @MuhammadNuman I posted a similar question based on Django backend, I tried your solution and couldn't resolve it yet. Please you please check it https://stackoverflow.com/questions/76887056/how-to-keep-django-web-session-active-in-react-native-webview – Prabhakaran Aug 11 '23 at 22:57
  • @Prabhakaran No. If your usecase is authentiacation, obtain a token on client side, then inject some JavaScript into the rendered WebView like this: https://github.com/react-native-webview/react-native-webview/issues/1548#issuecomment-1260811067 – Kazuya Gosho Aug 21 '23 at 08:50