Viewed   295 times

I developed an app for iOS and Android which accesses an HTML file from my webserver using the in-app browser (Webview).

I don't want that a user can access this file without using the app. Is there a possibility to detect, if the user is accessing the file with the app or directly via a browser on this smartphone / tablet / computer? I think that a solution with PHP is much better, because Javascript can be switched off. At least Google Analytics can differentiate between Safari and Safari (in-app). It should work with every version of iOS and Android.

Thanks for your help.


Solution

After many attempts I finally found a working solution for me!

iOS: You can detect the difference between Safari and the in-app browser using the user agent. Probably there's a nicer solution, but it works.

// Safari (in-app)
if ((strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile/') !== false) && (strpos($_SERVER['HTTP_USER_AGENT'], 'Safari/') == false) {
    echo 'Safari (in-app)';
}

Android: The package name from the app is stored in the PHP variable $_SERVER['HTTP_X_REQUESTED_WITH'].

// Android (in-app)
if($_SERVER['HTTP_X_REQUESTED_WITH'] == "com.company.app") {
    echo 'Android (in-app)';
}

As Tim van Elsloo already noted HTTP headers can be faked and this is not absolutely secure.

 Answers

3

I'm not sure about Android, but when you're using the iOS SDK's UIWebView, it sends the name and version of your app as part of the user agent (YourApp/1.0).

You can then use PHP to check if your in-app webview is being used or not:

if (strpos($_SERVER['HTTP_USER_AGENT'], 'YourApp/') !== false)

I think Android does something similar as well.

Monday, November 28, 2022
4

You can check for FBAN / FBAV in the user agent.

Check this link: Facebook user agent

Monday, August 1, 2022
 
sohan
 
2

There is few options to check it:

  1. Using Visibility API

  2. Using focus and blur events to detect browser tab visibility:

window.addEventListener("focus", handleBrowserState.bind(context, true));
window.addEventListener("blur", handleBrowserState.bind(context, false));

function handleBrowserState(isActive){
    // do something
}
  1. Using timers, as mentioned above
Monday, August 15, 2022
 
5

In 2016, this is impossible to achieve via Javascript alone

You can still use the approach on Android, but Apple made changes in iOS 9.2 that mean this is essentially useless on iOS devices.


How it used to work on iOS

The way this was done in the past was to attempt to open up the app in Javascript by setting window.location to the custom URI path that you wanted.

window.location = “imdb://title/tt3569230”;

App not installed

The problem with this is that when the app is not installed, it shows a ‘Cannot Open Page’ error. I’m sure you’ve all seen it. It’s the bane of deep linking. There was a glorious period during iOS 7 and iOS 8 where it was possible to bypass this, but the golden age has passed.

In order to avoid this, you had to add some Javascript to your page where you would redirect away to the App Store. This way, the user was not left with an error on the screen.

window.location = 'imdb://title/tt3569230';
setTimeout(function() {
  window.location = 'itms-apps://itunes.apple.com/us/app/imdb-movies-tv/id342792525'
}, 250);

App installed

When the app was installed, it would display the modal below, prompting the user if they want to open the app:

What happens now is that in iOS 9, Apple changed the Open in "[app]" modal from a Javascript blocking modal to a non-blocking modal. This means when you try to open up the app via a Javascript redirect to a custom URI scheme, the modal will no longer block Javascript from executing, with the result that the fallback redirect to the App Store will execute immediately before the user can tap the 'Open' button.

At Branch.io (full disclosure: I'm on the Branch team), we saw this coming in the iOS 9.2 betas and were hopeful that our Apple radars (bug reports) and influential partners could motivate Apple to resolve it before release. Unfortunately for iOS developers, it was not to be. Apple’s response to our concerns made perfectly clear what they wanted everyone to do: adopt Universal Links.


Solution for 2016

The only way to have a single link that works everywhere (including routing into apps if they are installed or to fallback web URLs if they are not) must include using Universal Links on iOS. Of course, Universal Links aren't actually supported everywhere in iOS yet, so there are specific edge cases where custom URI schemes are still required (Chrome and Gmail being two big examples). You'll need to detect these and build custom handling.

Most companies don't have the resources to devote a full-time engineer (or two) to this, which is why Pinterest, Tinder, Airbnb, Jet.com, Yummly, etc., have all adopted linking platforms like Branch.io or Firebase Dynamic Links.

Monday, August 15, 2022
 
binke
 
4

You are passing the wrong $data value into openssl_verify(). This value should be the full JSON string you get from Google Play, not the purchase token inside it. It is important that the JSON string is untouched, as even if you were to add a space or newlines to it, the signature would no longer work.

All you need to do in your code above is to change this line:

$result = openssl_verify($data["purchaseToken"], base64_decode($signature), $key);

to

$result = openssl_verify($data, base64_decode($signature), $key);

And you should get a success, assuming you're using the correct public key and the JSON purchase string is valid. I'm pretty sure your JSON string is not the original string from Google however, as the ones from Google do not contain newlines. It will be one long line of JSON text. Make sure that's what you are passing to openssl_verify().

Wednesday, September 7, 2022
 
sanal_s
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :