javascriptandroidgoogle-chromehttp-redirectappauth

Navigation is blocked error in Chrome 61+ on Android


I'm building a login page that, upon submitting and validation of the user credentials, opens up a native mobile application. Up till last week, I had this working cross mobile OS by using a custom scheme URI, something like:

function onLoginCallback() {
    const redirectUri = 'customscheme:/action?param1=val1&param2=val2';
    window.location.replace(redirectUri);
}

The login page is displayed in an IABT, short for In App Browser Tab.

However, since the release of version 61 of Chrome, this is approach is broken on Android. Chrome blocks the redirect because there's no apparent user action related to the redirect (see here for more information on the matter).

As a consequence, when executing the code above, I'll end up with a warning in the console:

Navigation is blocked: customscheme:/action?param1=val1&param2=val2

I've also tried updating the custom scheme url to an intent url but to no avail. Googling about this issue doesn't readily provide a clear solution, so I'm hoping anyone on can help me out.


Edit: Tried to reproduce the issue with the following scenario (as close as possible to the real life scenario):

Alas, this seems to be working. I would have expected that the inclusion of the jsonp call would cause Chrome to block the final redirect as it would not be able to identify it as a user initiated action.


Edit 2: Managed to get a reproducible scenario. We've set up a dummy endpoint, that upon request simply returns a 302 with the custom scheme in the Location header. This is blocked on all tries, except for the first one. That fact still boggles the mind. We're using the AppAuth for Android application to test the setup.

I'm opening a custom tab to the endpoint as shown below. The code is taken from this answer.

void launchTab(Context context, Uri uri){
    final CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
        @Override
        public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient client) {
            final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
            final CustomTabsIntent intent = builder.build();
            client.warmup(0L); // This prevents backgrounding after redirection
            intent.launchUrl(context, uri);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    CustomTabsClient.bindCustomTabsService(context, "com.android.chrome", connection);
}

Solution

  • We ended up implementing our login and registration forms with a classic post-redirect-get pattern.

    The server responds with a 302 to the custom URI scheme. Because in this setup there's no asynchronous execution between the user submitting the form and the browser receiving a redirect, Chrome correctly identifies the chain of actions as trusted and thus will not block the navigation.

    I realise this might not be the preferred solution for everyone. A possible alternative to support asynchronous execution flows is the use of universal links as these use regular http(s) schemes, to which redirects were (at the time of posting my question) not considered harmful by Chrome.