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¶m2=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¶m2=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):
- IABT displays a page with a single button
- Clicking the button fires an jsonp call to a mock endpoint
- The JSONP callback is executed and fires off a custom event
- An event handler for the custom event is triggered and redirects the browser to another mock endpoint
- That mock endpoint responds with a 302 to the custom deeplink scheme.
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);
}