Implementing web-based OAuth flow with Twitter API breaks for Android Nougat (API 25+)

restapi
android
oauth
api

#1

Nougat API 25 blocks Chrome redirects without user input now. When trying to implement OAuth flows for Twitter on Android, Chrome for API 25 and above now disallows redirects. This is especially problematic for third-party applications that want to implement OAuth flow with the Twitter API.

To repro:

  1. Sign-out of Twitter, clear browser.
  2. Initiate an OAuth flow to Twitter on Android 7.0 (with Chrome, not Firefox) using this project
  3. Sign-in to Twitter (for the first time, logged-in users have no problem)
  4. Redirect fails to happen (custom schemes such as oauth:// in Android and general http://-based links)

Here is the relevant documentation:

“The functionality has changed slightly in Chrome for Android, versions 25 and later…navigating… to a URI with a custom scheme such as paulsawesomeapp:// will not work even if the user has the appropriate app installed. Instead, you should implement a user gesture to launch the app via a custom scheme, or use the “intent:” syntax described in this article.”

I tried using intent:// as well but Chrome is blocking even intent:// links:

chromium: [INFO:CONSOLE(0)] “Navigation is blocked: intent://?oauth_token=ABCD123456&oauth_verifier=sHGGeYyAFUX3lYX2c5aG4qZUznWEMzyY#Intent;action=android.intent.action.VIEW;scheme=oauth;package=com.codepath.apps.restclienttemplate;end”, source: https://api.twitter.com/oauth/authorize (0)
05-08 04:39:48.164 8314 8340 E chromium: [ERROR:gles2_cmd_decoder.cc(2475)] [.RenderWorker-0xd42

The solution appears to be that the Twitter redirects on OAuth when Android is detected may need to prompt the user to redirect before happening:

"And Chrome doesn’t launch an external app for a given Intent URI in the following cases.

  • When the Intent URI is redirected from a typed in URL.
  • When the Intent URI is initiated without user gesture."

Same discussion here:


#2

I’m using an embedded Webview in api 25 and don’t get this issue. It redirects back as expected and I carry on as normal


#3

I have not heard of this as a widespread issue (yet) - I can certainly ask the Twitter Kit for Android folks to look and see if this is an issue they are hitting with the sign-in functionality, but as yet I’m not aware of this being a blocker. Thanks for the Chromium link and example project.


#4

As a workaround you could use a webpage as a callback and prompt the user to 'launch the app" with a tap of a button.


#5

This all seems related:


“One other potential option is to capture the redirect parameters on your backend, and then respond to the browser with a 302 redirect to your custom scheme. As this maintains a single redirect “chain” from a user action, this should be permitted.”


#6

I guess the answer is to use an embedded WebView in your app rather than a Chrome Custom Tab which is a different activity.


#7

Please don’t. The single biggest reason I’ll never use your app is forcing me to manually log-in instead of using the existing Chrome cookie jar.


#8

But that’s the point here, that it looks like Chrome Custom Tabs may not be an option


#9

My reading of all the resources in this thread is Chrome Custom Tabs are very much an option when you use explicit user actions to transition between apps. Plus with Google’s authentication requiring that you don’t use web view, you would have to maintain multiple flows to have both Twitter and Google as providers.


#10
  1. I have more data after doing experiments this whole week. Most of the problems may be related to using the Android emulators (both the Google-supplied x86 and Genymotion) with the Twitter OAuth. It turns out that the WebView browser that comes with API 24+ ships with an implementation that has been removed from Android Open Source. As a result, this simple implementation is not fully “feature” compatible, which includes the inability to launch custom intent:// URLs and/or launch apps directly from WebViews.

  2. Using Chrome works with emulators to test the OAuth flow, but not always. Twitter provides an “Authorize app” button before doing a redirect. Usually this user input is sufficient to trigger a redirect, but not always. It seems that the mouse click needs to happen within a certain interval (indications that is around 100ms?) to be considered a user gesture. It could be that running Chrome on an emulator incurs > 100 ms between redirects, so the OAuth flow is not always consistent and can sometime fail to redirect.

  3. If a fallback URL is specified and launching the intent somehow fails, ([via S.browser_fallback_url] (https://developer.chrome.com/multidevice/android/intents)), Chrome will launch the fallback URL specified. The drawback is that the original redirect OAuth parameters are not provided in this fallback URL.

  4. Twitter momentarily shows a “If your browser doesn’t redirect you please click here to continue.” Somehow if it detects a Chrome browser it hides this message away.

Can I make the suggestion that Twitter’s authorize step not try to hide the “If your browser doesn’t redirect you please click here to continue.” This way if the redirect somehow fails to resolve, then either the page will be stuck on the loading state of “Redirecting you back to the application”. The problem with a fallback URL is supplied, the OAuth token/verifier information is lost, so the user is forced to retry (which usually fixes the problem).

Check out the difference in implementations of the WebView shells in the shouldOverrideUrlLoading() call – the Android for Chrome appears to be the one that is being used in pre Android 7.0 devices. The Chrome one basically just returns false and does not appear to enable apps to be launched.