Skip to content

Documentation Clarity Needed: Android App Links (HTTPS callbacks) behave differently in Custom Tabs/Auth Tabs vs. External Browser #183

@Arthur-Zaripov

Description

@Arthur-Zaripov

Description

Hello,

Thank you for this essential package. I ran into a common issue during implementation that highlighted a discrepancy between how deep links/App Links are handled in the standard browser versus the environment used by flutter_web_auth_2 (Chrome Custom Tabs or Auth Tabs on Android).

The existing documentation mentions: "If you want to have a callback URL with http or https scheme, you also need to specify a host etc. See c:geo as an example for this."

This implies that HTTPS callbacks should work with proper configuration (autoVerify=true in AndroidManifest.xml and AASA/Asset Links setup).

The Problem on Android:

When using callbackUrlScheme: 'https', the authentication flow typically fails to return to the app because Chrome Custom Tabs (CCT) prioritize loading http(s) URLs as web navigation within the CCT context, even if the user has verified App Links setup and a "user gesture" (click) is involved. CCT ignores the operating system's instruction to launch the host application for verified HTTPS links during the OAuth redirect flow.

The Reliable Solution on Android:

The only reliable and universally supported method for OAuth callback on Android using CCT/Auth Tabs is to use a custom URL scheme (e.g., myapp://callback). This forces the browser to fail the navigation attempt and hand the URL back to the operating system, which then correctly launches the app via its intent-filter.

Suggestion:

Please update the documentation (e.g., the README.md file) to explicitly clarify the following points for Android users:

  1. HTTPS schemes (http/https) are generally unreliable for callbackUrlScheme on Android when using this package. While App Links work in the external system browser, they typically fail within the Chrome Custom Tabs environment used internally by the package.

  2. Using a custom scheme (e.g., myapp) is the recommended and robust approach for Android.

  3. Users must configure their OAuth provider's redirect URI to match this custom scheme (e.g., myapp://auth).

  4. Also, take a look at this documentation:
    (https://developer.chrome.com/docs/android/custom-tabs/guide-warmup-prefetch#open_webpages_in_native_apps/)

By default, Custom Tabs opens links in the respective Android application if installed. However, once a CustomTabsServiceConnection has been established, this behavior stops working and all URLs open in Custom Tabs instead. For an improved user experience, we recommend re-enabling this behavior using the following code:

CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder()
    .setSendToExternalDefaultHandlerEnabled(true)
    .build();

Adding this clarity will prevent developers from spending significant time debugging why their perfectly configured Android App Links aren't working within the flutter_web_auth_2 flow.

Thank you!

Minimal Reproduction

Steps to reproduce the behaviour:

  1. Use the following code:
      Future<void> performOAuth() async {
    final authLinkResponse = await _apiClient.dio.get('/api/v1/auth/oauth-provider/link');
    
    final authCodeResponse = await FlutterWebAuth2.authenticate(
      url: authLinkResponse.data['url'],
      callbackUrlScheme: 'https',
    );
    
    final code = Uri.parse(authCodeResponse).queryParameters['code'];
    
    final data = {'code': code};
    
    final response = await _apiClient.dio.post('/api/v1/auth/oauth-provider/login', data: data);
    
    _apiClient.setToken(response.data['access_token']);

}
```

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
    <application
        android:label="testapp"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:taskAffinity=""
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
            android:exported="true">
            <intent-filter android:label="flutter_web_auth_2" android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="https" android:host="testapp.ru"/>         
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
    <!-- Required to query activities that can process text, see:
         https://developer.android.com/training/package-visibility and
         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.

         In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT"/>
            <data android:mimeType="text/plain"/>
        </intent>
    </queries>
</manifest>

Exception or Error

After successful authentication you are redirected to the website instead of the app.

Expected Behaviour

After successful authentication, you should be redirected to the app.

Screenshots

No response

Additional context

No response

Device

Android Emulator (Pixel 5, API 31)

OS

Windows 10

Browser

Chrome

Flutter version

3.35.4

flutter_web_auth_2 version

^5.0.0-alpha.7

Checklist

  • I have read and followed the entire troubleshooting guide and it has not provided the solution I need.
  • I have provided all the information I can.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions