Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS cookie fails to reload or restore when reopening the app | Only for specific sites #510

Closed
5 tasks done
breebee opened this issue Sep 6, 2020 · 21 comments
Closed
5 tasks done
Labels
bug Something isn't working

Comments

@breebee
Copy link

breebee commented Sep 6, 2020

Environment

Technology Version
Flutter version 1.22.0-9.0.pre
Plugin version 49.0.4
Android version
iOS version 11 pro Max iOS-13-4 (simulator)
Xcode version 11.4.1

Device information:

Description

Expected behavior:
It is expected when logged into a webview using the cookie manager all webviews across the app are logged in sharing cookies. Reopening the app after closing it should restore cookies and sessions.

Current behavior:
Only on iOS everything works in sync but when you close the app and re open it, it fails to reload/restore the cookies and sessions. Android does not seem to face this issue.

Steps to reproduce

Use a websites url's for the webviews with a site who may have too many cookies / malformatted cookies which may cause ios to get stuck / timeout when restoring/reopening the app.

@breebee breebee added the bug Something isn't working label Sep 6, 2020
@breebee
Copy link
Author

breebee commented Sep 6, 2020

Maybe an SSL tls version or something on the server end? I have some joomla websites that the ios issue is not present and some where the issue is present. However i cannot find the difference 2 are practically identical where 1 works and the other does not. Any clues to where I should look? Same 3 default joomla cookies are set so its not the cookies.

@scottk
Copy link

scottk commented Sep 6, 2020

Also having this problem. Have the same app on Android/iOS. Android works perfectly, iOS treats each session as new

@scottk
Copy link

scottk commented Sep 6, 2020

After looking through the code, this fixed it for me:

I had sharedCookiesEnabled set to true, and incognito to false. In the code, if that combination is true, it uses a nonPersistent session type which is a bit confusing, which caused the session to not persist.

Setting sharedCookiesEnabled to false and incognito to false fixed this for me on iOS.

@breebee
Copy link
Author

breebee commented Sep 6, 2020

@scottk Your suggestion did not work in this case, the shared cookies work with known websites such as twitter ect on ios. It is only with some sites, even when comparing some of my servers running nginx vs openlitespeed it is hard to find the cause. 2 instances running openlitespeed show one working and the other not persisting sessions. Any hints to possible cause such as ciphers ect. please share. I will continue to solve this mystery

@khacnha
Copy link

khacnha commented Sep 8, 2020

Me too.
Cookie in Webview not working in ios.
But i go to cookie setting in my iphone -> touch disable cookie and enable again =>i check my app, cookie in webview is working.
I dont know why @.@

@breebee
Copy link
Author

breebee commented Sep 8, 2020

@khacnha your issue seems related to app state/cache that resets when system parameter is changes. Try the "Device -> Erase all content and settings" on the simulator and test your app from scratch.

I solved my mystery however!! It appears a redirect if statement on my php end of joomla causes the iOS cookies to not get fetched correctly when reopening the app. I have no clue how that can be the culprit, nor how to keep both my redirect active to maintain cookie functionality. Possibly force an "await" for the cookies if possible could solve something however I am going to find an alternative solution for my redirect in the mean time. If someone ever comes up with a solution to enforce cookies being fetched until finished please post here.

@breebee breebee closed this as completed Sep 8, 2020
@breebee breebee reopened this Sep 8, 2020
@arnaudelub
Copy link

i have the same issue with v4.0.4, i have been reverting to version 3.3.0 for iOS and now it's working correclty.
This is a really nice plugin, but for Android text selection is not working as it should, keep on the good work Lorenzo

@rahuldev91
Copy link

rahuldev91 commented Oct 29, 2020

@breebee did get the solution I also have a similar problem,
for the master server cookies are working fine but on the production server cookies are not setting, how to debug this issue?

@breebee
Copy link
Author

breebee commented Nov 9, 2020

Yes, the cookie issue on my end was due to redirects for the app which must have lost the session or something in the process. iOS and android both handle cookies correctly with inappwebview, you must find the point where your code is different in dev to prod.

*** Make sure you clear everything "Device -> Erase all content and settings" on the simulator and test your app from scratch. When making changes, I found this to be an issue when testing changes to find the main cause.

@khacnha
Copy link

khacnha commented Nov 10, 2020

i had to switch to use flutter webview and it working: https://pub.dev/packages/webview_flutter

I think it's a potential error, not the way we set cookies. Since I have tried many ways from setting cookies with javascript to setting cookies on php, all of them failed in ios

@breebee
Copy link
Author

breebee commented Nov 13, 2020

@khacnha if you want me to give a look, send me the url and test credentials to see if i can persist the cookie on ios for you from the inappwebview side if your convinced it is not from your server.

@khacnha
Copy link

khacnha commented Nov 17, 2020

@breebee This is url demo: http://mutu.vietprojectgroup.com/webview/search
We store search terms in cookies. you can try it.

@breebee
Copy link
Author

breebee commented Nov 17, 2020

@khacnha works for me dude. I close app, reopen works, switching urls works, power off phone and restart app works.

using iphone 8 (13.4.1)

initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
javaScriptEnabled: true,
cacheEnabled: true,
supportZoom: false,
clearCache: false,
transparentBackground: true,
debuggingEnabled: true,
useShouldOverrideUrlLoading: true,
),
),

[✓] Flutter (Channel dev, 1.23.0-13.0.pre, on Mac OS X 10.15.6 **** x86_64, locale en-US)
• Flutter version 1.23.0-13.0.pre at *****
• Framework revision 4fa4f91d5c (6 weeks ago), 2020-10-06 12:31:25 -0700
• Engine revision 443cd5a1e1
• Dart version 2.11.0 (build 2.11.0-186.0.dev)

[!] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
• Android SDK at *****
• Platform android-30, build-tools 29.0.3
• Java binary at: /Library/Java/JavaVirtualMachines/adoptopenjdk-14.jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment AdoptOpenJDK (build 14.0.2+12)
✗ Android license status unknown.
Run flutter doctor --android-licenses to accept the SDK licenses.
See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.

[✓] Xcode - develop for iOS and macOS (Xcode 11.4.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 11.4.1, Build version 11E503a
• CocoaPods version 1.9.1

[!] Android Studio (not installed)
• Android Studio not found; download from https://developer.android.com/studio/index.html
(or visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions).

[✓] IntelliJ IDEA Community Edition (version 2020.2.3)
• IntelliJ at /Applications/IntelliJ IDEA CE.app
• Flutter plugin installed
• Dart plugin version 202.7319.5

[✓] Connected device (2 available)
• iPhone 8 (mobile) • ******* • ios • com.apple.CoreSimulator.SimRuntime.iOS-13-4 (simulator)
• macOS (desktop) • macos • darwin-x64 • Mac OS X 10.15.6 **** x86_64

! Doctor found issues in 2 categories.
Process finished with exit code 0

@jreliberty
Copy link

jreliberty commented Nov 29, 2021

I'm facing the same problem on a real device... Anyone found a solution ?

@AleksandarSavic95
Copy link

AleksandarSavic95 commented Mar 15, 2022

@jreliberty me too! Did you find any solution to this?
@pichillilorenzo can you help us out? I don't want to create new issues, so I am writing here.

I have this website for which the Safari browser doesn't keep the session. So, I log in, close Safari and reopen it, and I'm not logged in. :/ On android (Chrome browser) it works.
So, I would like to know is it possible to have this one "cached" instance of the webview's "state" and always reuse that. I don't need to read the contents myself, I just need to not clear everything on every closing of the app (webview).

As I see that your plugin is very rich in features, I thought you can tell me if I can get the webview to preserve session when I close and reopen my app. Or at least give me advice in which direction I should research, if you know of course.

Thanks, to both of you!

P.S.
[[[ don't need to read this if you have positive news/advice on the text I wrote above ]]]
I commented on this issue for the webivew_flutter plugin (the "official" Flutter webview) so you can check it out and tell me if I am on the right track. I added the code from the PR into my flutter project but it didn't resolve the session issue.

@Abdallah-El-Feky
Copy link

Hey man, hope u doing great I have the same issue you had before would pls help me solve it here's my code
import 'dart:async';
import 'dart:collection';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:carwash/connectionStatus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:location/location.dart';

//void main() => runApp(const MaterialApp(home: MyHomePage()));
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
ByteData data =
await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
SecurityContext.defaultContext
.setTrustedCertificatesBytes(data.buffer.asUint8List());
//await Permission.storage.request();

if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);

var swAvailable = await AndroidWebViewFeature.isFeatureSupported(
    AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE);
var swInterceptAvailable = await AndroidWebViewFeature.isFeatureSupported(
    AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST);

if (swAvailable && swInterceptAvailable) {
  AndroidServiceWorkerController serviceWorkerController =
      AndroidServiceWorkerController.instance();

  await serviceWorkerController
      .setServiceWorkerClient(AndroidServiceWorkerClient(
    shouldInterceptRequest: (request) async {
      print(request);
      return null;
    },
  ));
}

}

runApp(const MaterialApp(
home: MyHomePage(),
debugShowCheckedModeBanner: false,
));
}

class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);

@OverRide
State createState() => _MyHomePageState();
}

class _MyHomePageState extends State {
late Timer timer;
String data = "";

Future readData() async {
var uri = "https://carwashapp.online/datanotify.php";

var response = await http.get(Uri.parse(uri));
if (response.statusCode == 200) {
  // print('object1');
  // print(response.statusCode);
  print(response.body);
  // print('object2');
  var statusdata = response.body;

  setState(() {
    data = statusdata;
  });
  // print('test1');
  // print(data);
  // print('test2');
}
//   if (preferences.getString("randomtime") != null) {
//   if (preferences.getString("randomtime") == "true") {
//     NotificationApi.showNotification(
//     body: "this is body",
//     id: 3,
//     payload: "this is payload",
//     title: "this is title",
//     );
//    }
//  }

}

late bool _serviceEnabled;
late PermissionStatus _permissionGranted;
late bool _main;
final GlobalKey webViewKey = GlobalKey();
var connectionStatus;
InAppWebViewController? webViewController;
InAppWebViewGroupOptions options = InAppWebViewGroupOptions(

  crossPlatform: InAppWebViewOptions(

      clearCache: false,
      javaScriptEnabled: true,
      useOnLoadResource: true,
      cacheEnabled: true,
      supportZoom: false,
      preferredContentMode: UserPreferredContentMode.MOBILE,
      useShouldOverrideUrlLoading: true,
      mediaPlaybackRequiresUserGesture: false),
  android: AndroidInAppWebViewOptions(
    cacheMode: AndroidCacheMode.LOAD_CACHE_ELSE_NETWORK,
    useHybridComposition: true,
  ),
  ios: IOSInAppWebViewOptions(
    sharedCookiesEnabled: true,
    allowsInlineMediaPlayback: true,


  ));

// ignore: non_constant_identifier_names
late Future WKwebview;

late PullToRefreshController pullToRefreshController;
late ContextMenu contextMenu;
String url = "";
final urlController = TextEditingController();

@OverRide
void initState() {
getData();

timer = Timer.periodic(const Duration(seconds: 5), (timer) => readData());

notificationWidget.init();

super.initState();
checkLocationServicesInDevice();
firstCheck();

contextMenu = ContextMenu(
    menuItems: [
      ContextMenuItem(
          androidId: 1,
          iosId: "1",
          title: "Special",
          action: () async {
            print("Menu item Special clicked!");
            print(await webViewController?.getSelectedText());
            await webViewController?.clearFocus();
          })
    ],
    options: ContextMenuOptions(hideDefaultSystemContextMenuItems: false),
    onCreateContextMenu: (hitTestResult) async {
      print("onCreateContextMenu");
      print(hitTestResult.extra);
      print(await webViewController?.getSelectedText());
    },
    onHideContextMenu: () {
      print("onHideContextMenu");
    },
    onContextMenuActionItemClicked: (contextMenuItemClicked) async {
      var id = (Platform.isAndroid)
          ? contextMenuItemClicked.androidId
          : contextMenuItemClicked.iosId;
      // print("onContextMenuActionItemClicked: " +
      //     id.toString() +
      //     " " +
      //     contextMenuItemClicked.title);
    });

pullToRefreshController = PullToRefreshController(
  options: PullToRefreshOptions(
    color: Colors.blue,
  ),
  onRefresh: () async {
    if (Platform.isAndroid) {
      webViewController?.reload();
    } else if (Platform.isIOS) {
      webViewController?.loadUrl(
          urlRequest: URLRequest(url: await webViewController?.getUrl()));
    }
  },
);

}

getData() async {
await readData();
}

@OverRide
void dispose() {
super.dispose();
}
Future setHttpAuthCredential(
{required URLProtectionSpace protectionSpace,
required URLCredential credential}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol);
args.putIfAbsent("realm", () => protectionSpace.realm);
args.putIfAbsent("port", () => protectionSpace.port);
args.putIfAbsent("username", () => credential.username);
args.putIfAbsent("password", () => credential.password);
var _channel;
await _channel.invokeMethod('setHttpAuthCredential', args);
}
Future checkLocationServicesInDevice() async {
Location location = new Location();

_serviceEnabled = await location.serviceEnabled();

if (_serviceEnabled) {
  _permissionGranted = await location.hasPermission();

  if (_permissionGranted == PermissionStatus.granted) {
    _main = true;
    location.onLocationChanged.listen((LocationData currentLocation) {
      // print(currentLocation.latitude.toString() +
      //     " " +
      //     currentLocation.longitude.toString());
    });
  } else {
    _permissionGranted = await location.requestPermission();

    if (_permissionGranted == PermissionStatus.granted) {
      print('user allowed');
      _main = true;
    } else {
      SystemNavigator.pop();
    }
  }
} else {
  _serviceEnabled = await location.requestService();

  if (_serviceEnabled) {
    _permissionGranted = await location.hasPermission();

    if (_permissionGranted == PermissionStatus.granted) {
      print('user allowed before');
      _main = true;
    } else {
      _permissionGranted = await location.requestPermission();

      if (_permissionGranted == PermissionStatus.granted) {
        print('user allowed');
        _main = true;
      } else {
        SystemNavigator.pop();
      }
    }
  } else {
    SystemNavigator.pop();
  }
}

}

Future firstCheck() async {
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
connectionStatus = true;
// print("connected $connectionStatus");
}
} on SocketException catch () {
connectionStatus = false;
//print("not connected $connectionStatus");
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (
) => ConnectionStatus()),
(route) => false);
}
}

@OverRide
Widget build(BuildContext context) {
return Scaffold(
body: WillPopScope(
onWillPop: () async {
setState(() {
webViewController?.goBack();
});

          return false;
        },
        child: Scaffold(
            resizeToAvoidBottomInset: true,
            body: SafeArea(
                child: Column(children: <Widget>[
              Expanded(
                child: Stack(
                  children: [
                    InAppWebView(
                      key: webViewKey,

                      initialUrlRequest: URLRequest(
                          url: Uri.parse("https://carwashapp.online/")),
                      // initialFile: "assets/index.html",
                      initialUserScripts:
                          UnmodifiableListView<UserScript>([]),
                      initialOptions: options,

                      pullToRefreshController: pullToRefreshController,
                      onLoadHttpError: (InAppWebViewController controller,
                          Uri? url, int i, String s) async {
                        /** instead of printing the console message i want to render a static page or display static message **/
                        webViewController?.loadFile(
                            assetFilePath: "assets/error.html");
                      },
                      onWebViewCreated: (controller) {
                        webViewController = controller;
                      },
                      onLoadStart: (controller, url) {
                        setState(() {
                          this.url = url.toString();
                          urlController.text = this.url;
                        });
                      },

                      androidOnGeolocationPermissionsShowPrompt:
                          (InAppWebViewController controller,
                              String origin) async {
                        if (_main == true) {
                          return Future.value(
                              GeolocationPermissionShowPromptResponse(
                                  origin: origin,
                                  allow: true,
                                  retain: true));
                        } else {
                          checkLocationServicesInDevice();
                        }
                      },
                      androidOnPermissionRequest:
                          (controller, origin, resources) async {
                        return PermissionRequestResponse(
                            resources: resources,
                            action: PermissionRequestResponseAction.GRANT);
                      },
                      shouldOverrideUrlLoading:
                          (controller, navigationAction) async {
                        var uri = navigationAction.request.url!;

                        if (![
                          "http",
                          "https",
                          "file",
                          "chrome",
                          "data",
                          "javascript",
                          "about"
                        ].contains(uri.scheme)) {
                          if (await canLaunchUrlString(uri.toString())) {
                            // Launch the App
                            await launchUrlString(uri.toString(),
                                mode: LaunchMode.externalApplication);
                            // and cancel the request
                            return NavigationActionPolicy.CANCEL;
                          }
                        }

                        return NavigationActionPolicy.ALLOW;
                      },
                      onLoadStop: (controller, url) async {
                        firstCheck();
                        pullToRefreshController.endRefreshing();
                        setState(() {
                          this.url = url.toString();
                          urlController.text = this.url;
                        });
                      },
                      onLoadError: (controller, url, code, message) {
                        pullToRefreshController.endRefreshing();
                      },

                      onUpdateVisitedHistory:
                          (controller, url, androidIsReload) {
                        setState(() {
                          this.url = url.toString();
                          urlController.text = this.url;
                        });
                      },
                      onConsoleMessage: (controller, consoleMessage) {
                        print(consoleMessage);
                      },
                    ),
                  ],
                ),
              ),
            ])))));

}
}

class notificationWidget {
static final _notifications = FlutterLocalNotificationsPlugin();

static Future init({bool scheduled = false}) async {
var initAndroidSettings =
AndroidInitializationSettings('mipmap/ic_launcher');
var ios = IOSInitializationSettings();
final settings =
InitializationSettings(android: initAndroidSettings, iOS: ios);
await _notifications.initialize(settings);
}

static Future shownotification(
{var id = 0, var title, var body, var payload}) async =>
_notifications.show(id, title, body, await notificationDetails());

static notificationDetails() async {
return const NotificationDetails(
android: AndroidNotificationDetails('channel id', 'channel name',
importance: Importance.max),
iOS: IOSNotificationDetails());
}
}

@Abdallah-El-Feky
Copy link

@khacnha if you want me to give a look, send me the url and test credentials to see if i can persist the cookie on ios for you from the inappwebview side if your convinced it is not from your server.

Would u pls help me

@Abdallah-El-Feky
Copy link

@breebee Would u pls help me

@breebee
Copy link
Author

breebee commented Aug 18, 2022

@Abdallah-El-Feky first check if cookies work on other sites to determine if the issue is on your servers end

@Anarchid
Copy link

Anarchid commented Jul 8, 2024

Seems to be still happening

Copy link

github-actions bot commented Oct 3, 2024

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 3, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

9 participants