apache / cordova-plugin-inappbrowser

Apache Cordova InAppBrowser Plugin
https://cordova.apache.org/
Apache License 2.0
1.12k stars 2.15k forks source link

OverrideUserAgent changes user agent for Cordova view too #372

Open dariofilipaj opened 5 years ago

dariofilipaj commented 5 years ago

This bug is already known as noted on https://github.com/apache/cordova-plugin-inappbrowser/pull/94 and https://github.com/ionic-team/ionic/issues/11790 but i couldn't find any active issue to track progress.

What happened: In our specific case we use Ionic Platform to programmatically check if platform is either "iOS" or "Android". In same time we use InAppBrowser for oauth on our server. Therefor we need to use OverrideUserAgent property.

Unfortunately as soon as we add OverrideUserAgent property Platform module doesn't work properly. It detects iOS and Android as desktop platform.

Expected behavior Platform module should still be usable and properly detect iOS and Android platform.

Is there any progress on this issue or any suggestion how to avoid it?

EDIT: Same behavior on Android and iOS but Android can be avoided by using Crosswalk plugin.

jacobg commented 5 years ago

Is this the same issue?

https://issues.apache.org/jira/projects/CB/issues/CB-13990

janpio commented 5 years ago

Yes, I closed that now. For reference the description:

Currently, there is an OverrideUserAgent setting that will affect both the core webview (i.e., that runs the main web app) as well as the inappbrowser plugin. It would be great to be able to separate into two setting, i.e., have an InAppBrowserOverrideUserAgent. That's because I'd like my own app to know the true useragent, while sending a different useragent to other web sites.

vivekgupta1008 commented 5 years ago

+1, facing same issue. In my case the UI breaks at some places.

goran-hc commented 5 years ago

+1, same issue here.

jacobg commented 5 years ago

In case anyone is interested, I wrote a script that patches the plugin in my build, as follows:

patchInAppBrowser.js

var replace = require("replace");
var path = require("path");

// Patch for these issues, on top of git master:
//    https://issues.apache.org/jira/projects/CB/issues/CB-13990
// Until a new version is published to npm with the fixes

module.exports = function(context) {

    var iosFile = path.resolve(context.opts.projectRoot + "/platforms/ios/Fareclock/Plugins/cordova-plugin-inappbrowser", "CDVUIInAppBrowser.m");

    var filesToPatch = [
        iosFile,
        path.resolve(context.opts.projectRoot + "/platforms/android/app/src/main/java/org/apache/cordova/inappbrowser", "InAppBrowser.java")
    ];

    replace({
        regex: "\"OverrideUserAgent\"",
        replacement: "\"InAppBrowserOverrideUserAgent\"",
        paths: filesToPatch,
        recursive: false,
        silent: true,
    });
};

And in config.xml, add this line:

<hook src="build/patchInAppBrowser.js" type="after_prepare" />

Then you set InAppBrowserOverrideUserAgent preference in config.xml like this:

<preference name="InAppBrowserOverrideUserAgent" value="Mozilla/5.0" />
haskasu commented 5 years ago

@jacobg Your solution works great! Thanks for sharing.

Xample commented 5 years ago

As platform will use the user agent, you can conditionally set a user agent for iOS and another for Android

config.xml

<?xml version='1.0' encoding='utf-8'?>
<widget id="ch.tpg.boldor" version="1.43.4-dev" xmlns="http://www.w3.org/ns/widgets"
    <platform name="android">
        <preference name="OverrideUserAgent" value="Mozilla/5.0 (Linux; Android 9; SM-G960F Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Mobile Safari/537.36" />
    </platform>
    <platform name="ios">
        <preference name="OverrideUserAgent" value="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/604.1" />
</platform>
</widget>
jacobg commented 4 years ago

I just switched from UIWebView to WkWebView, and so the patch script in my comment above just needs to be changed from editing CDVUIInAppBrowser.m to CDVWKInAppBrowser.m.

However, I am noticing that the first time the in-app browser is opened it uses the default useragent string instead of the OverrideUserAgent or InAppBrowserOverrideUserAgent string. Subsequent opens of the in-app browser use the overridden user agent string. It's really weird. I can debug in the code, and see that the overridden user agent string gets set by CDVUserAgentUtil to the standardUserDefaults, but for some reason the WkWebView is not picking it up the first time.

Anyone else observe that happen as well?

faisakhtar commented 4 years ago

@jacobg I am also experiencing the same problem with the overridden user agent not getting picked up the first time you open the in app browser, but is correctly picked up on subsequent opens. Did you ever find a solution to this?

jacobg commented 4 years ago

@faisakhtar Yes, I wrote a wrapper function that checks if in-app browser has been opened at least once. If not, then it opens and closes the window one time, before opening it again. It happens pretty fast, so it's not so noticeable.

Here is example code. I modified it a little from my own application to make it more generic, so there may be a typo.

ensureIabOpenedAtLeastOnce (args) {
  // cordova-plugin-inappbrowser seems to have a bug on iOS WkWebView with overriding user agent first time
  // window is opened: https://github.com/apache/cordova-plugin-inappbrowser/issues/372#issuecomment-584930604
  if (!this.iabOpenedAtLeastOnce && isIos && window.cordova) {
    const win = openIabWindow(['about:blank'])
    win.addEventListener('loadstop', () => {
      win.addEventListener('exit', () => openIabWindow(args))
      win.close()
    })
  } else {
    openIabWindow(args)
  }
}
openIabWindow (args) {
  this.iabOpenedAtLeastOnce = true
  return window.open(...args)
}
faisakhtar commented 4 years ago

Thank you for the example work around. Opening, closing and then re-opening the in app browser automatically does indeed get around the problem, but not the ideal user experience. For me this was taking a couple of seconds and it didn't look great with the open/close/open transitions.

I ended up assigning a custom useragent to the webview in the "createViews" function of CDVWKInAppBrowser.m. This works for me as I am appending a useragent in my application which I simply strip out in this function and the custom useragent persists. Appears to work ok but an official solution would be most welcome :).

NSString* iabUserAgent = [[CDVUserAgentUtil originalUserAgent] stringByReplacingOccurrencesOfString:@"MyAppendedUserAgent" withString:@""]; self.webView.customUserAgent = iabUserAgent;

goodspeed666 commented 4 years ago

As platform will use the user agent, you can conditionally set a user agent for iOS and another for Android

config.xml

<?xml version='1.0' encoding='utf-8'?>
<widget id="ch.tpg.boldor" version="1.43.4-dev" xmlns="http://www.w3.org/ns/widgets"
    <platform name="android">
        <preference name="OverrideUserAgent" value="Mozilla/5.0 (Linux; Android 9; SM-G960F Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Mobile Safari/537.36" />
    </platform>
    <platform name="ios">
        <preference name="OverrideUserAgent" value="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/604.1" />
</platform>
</widget>

Thank you @Xample. You saved my life !

Naveendodda commented 3 years ago

Hi Team, Android working fine IOS not working it showing Empty screen

HarelM commented 2 years ago

Has anyone been able to solve this? I'm trying to find a way to make the login to openstreetmap work from the app using a google account. This fails since google doesn't allow the cordova user agent...

erisu commented 2 years ago

@HarelM I think the user agent has nothing to do with it.

Read this post from Google.

https://developers.googleblog.com/2021/06/upcoming-security-changes-to-googles-oauth-2.0-authorization-endpoint.html

As part of that work, we recently introduced a new secure browser policy prohibiting Google OAuth requests in embedded browser libraries commonly referred to as embedded webviews. All embedded webviews will be blocked starting on September 30, 2021.

Embedded webviews implementing or extending Android WebView do not comply with Google's secure browser policy for its OAuth 2.0 Authorization Endpoint.

I think it is the Google's secure browser policy that is blocking it.

jacobg commented 2 years ago

If you set the user agent, it bypasses Google's webview block. Our app had to do this, because it's a kiosk app and it's crucial that the google login info is not saved into the browser. Only by using a webview can we delete all browser state of the login after it completes.

My comment above is how we deal with user agent not getting set the first time: https://github.com/apache/cordova-plugin-inappbrowser/issues/372#issuecomment-621938359

HarelM commented 2 years ago

Thanks for the info @erisu & @jacobg! I'm looking into adding the cordova-plugin-oauth to solve this issue. This seems to be working from a POC I was just able to work so I might be able to remove IAB dependency altogether. I don't like the custom scheme too much, it doesn't support androidx and collides with the deeplinks plugin I'm using, but it seems to pass the 403 google error.

karimaswan22 commented 2 months ago

you can always do git clone https://github.com/apache/cordova-plugin-inappbrowser and in inappbrowser.java search "appendUserAgent"
what you need to do is appending your own useragent

            settings.setUserAgentString("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36");

            // if (overrideUserAgent != null) {
            //     settings.setUserAgentString(overrideUserAgent);
            // }
            // if (appendUserAgent != null) {
            //     settings.setUserAgentString(settings.getUserAgentString() + " " + appendUserAgent);
            // }

            //Toggle whether this is enabled or not!