darwin-morocho / flutter-facebook-auth

A flutter plugin to add login with facebook in your flutter app
193 stars 127 forks source link

Crash on release build in Android 11 #138

Closed itssidhere closed 2 years ago

itssidhere commented 2 years ago

Hello, the plugin works well in debug mode but after trying it out on release mode I get this error.

WhatsApp Image 2021-08-23 at 9 51 05 AM

itssidhere commented 2 years ago

This is from the facebook documentation.

To avoid a disrupted user experience, please use the following checklist: Ensure that you have upgraded to version 8.2.0 (or later) of the Facebook SDK for Android. If your app is built to target Android 11 (API level 30) and your users are on Android 11, alternative non-webview login mechanisms provided by the SDK will not work unless you upgrade to or past 8.2.0.

darwin-morocho commented 2 years ago

flutter_facebook_auth: ^3.5.0 uses the facebook sdk 11.0.0

darwin-morocho commented 2 years ago

I have updated flutter_facebook_auth to 3.5.1 to use the android facebook sdk 11.1.1 but I think that you should open the issue here https://github.com/facebook/facebook-android-sdk because the issue is not this plugin

itssidhere commented 2 years ago

@darwin-morocho have you seen this?

https://medium.com/androiddevelopers/package-visibility-in-android-11-cc857f221cd9

darwin-morocho commented 2 years ago

@darwin-morocho have you seen this?

https://medium.com/androiddevelopers/package-visibility-in-android-11-cc857f221cd9

This plugin does not start an activity maybe the facebook sdk on Android has this functionality so the issue is from the native sdk, in that case if we need to add some extra code in this plugin (Android side) please add a documentation of facebook or any other source related with this

itssidhere commented 2 years ago

@darwin-morocho Can we bubble up the exception by the SDK from method channel to the app? So we can handle the error by ourselves because right now it just crashes the app. I tried using try catch block but I think error is not bubbling up into the dart code.

darwin-morocho commented 2 years ago

If the app is working fine in debug mode maybe the problem is your release key hash in the console or any missing configuration in the Facebook developers console. Actually the plataform channels catch all events and errors according to Facebook documentacion. Unfortunally i don't have an Android 11 device to replicate the problem because I use a mac with M1 chip

itssidhere commented 2 years ago

Actually the app crashes in mi phones most of the time, works good on Samsung

darwin-morocho commented 2 years ago

Actually the app crashes in mi phones most of the time, works good on Samsung

according to your medium link you must add the queries for the Facebook app adding the Facebook package name or you can use loginBehavior parameter in the login method and use default falkback or webview when you app is running Android 11 or higher

seifkh97 commented 2 years ago

@itssidhere Yes same point it in release mode it only works on samsung. Can smne explain why?

darwin-morocho commented 2 years ago

@itssidhere Yes same point it in release mode it only works on samsung. Can smne explain why?

It seems a breaking change in the Facebook sdk on Android please check the documentation https://developers.facebook.com/docs/facebook-login/android/deprecating-webviews let me know if doing the changes in this documentation fix the problem

itssidhere commented 2 years ago

@darwin-morocho I tried that already but that doesn't seems to work

darwin-morocho commented 2 years ago

@darwin-morocho I tried that already but that doesn't seems to work

Please add your android manifest and your dart code.

Also add your MainActivty and your build.gradle

darwin-morocho commented 2 years ago

After check a similar issue on react native https://github.com/thebergamo/react-native-fbsdk-next/issues/64#issuecomment-870614017 it seems a problem with FlutterActivity so can you try this https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-557939158

itssidhere commented 2 years ago

@darwin-morocho

Manifest looks like this , though i have tried commenting customtab acitivity still no luck


<application android:name=".Application" android:icon="@mipmap/launcher_icon" android:roundIcon="@mipmap/ic_launcher_round" android:label="Mohalla" android:usesCleartextTraffic="true">
        <activity android:name=".MainActivity" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:windowSoftInputMode="adjustResize">
            <!--
            This keeps the window background of the activity showing
            until Flutter renders its first frame. It can be removed if
            there is no splash screen (such as the default splash screen
            defined in @style/LaunchTheme).
            -->
            <!-- <meta-data android:name="io.flutter.app.android.SplashScreenUntilFirstFrame" android:value="true" /> -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity>

<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>

 <activity android:name="com.facebook.FacebookActivity"
     android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
     android:label="@string/app_name" />
 <activity
     android:name="com.facebook.CustomTabActivity"
     android:exported="true">
     <intent-filter>
         <action android:name="android.intent.action.VIEW" />
         <category android:name="android.intent.category.DEFAULT" />
         <category android:name="android.intent.category.BROWSABLE" />
         <data android:scheme="@string/fb_login_protocol_scheme" />
     </intent-filter>
 </activity>

MainActivity

import android.content.Intent;
import io.flutter.embedding.engine.FlutterEngine;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterFragmentActivity;
import java.nio.ByteBuffer;
import io.flutter.plugin.common.ActivityLifecycleListener;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterFragmentActivity {

  private String sharedText;

  @Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    // GeneratedPluginRegistrant.registerWith(flutterEngine);
    super.configureFlutterEngine(flutterEngine);
    handleSendIntent(getIntent());

    new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "app.channel.shared.data").setMethodCallHandler(new MethodCallHandler() {
      @Override
      public void onMethodCall(MethodCall call, MethodChannel.Result result) {

        if (call.method.contentEquals("getSharedText")) {
          result.success(sharedText);
          sharedText = null;
        }
      }
    });

  }

  @Override
  protected void onNewIntent(Intent intent) {
    // Handle intent when app is resumed
    super.onNewIntent(intent);
    handleSendIntent(intent);
  }

  private void handleSendIntent(Intent intent){

    String action = intent.getAction();
    String type = intent.getType();

    if (Intent.ACTION_SEND.equals(action) && type != null) {
      if ("text/plain".equals(type)) {
        sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); // Handle text being sent
      }
    }
    }

}

Build.gradle

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new FileNotFoundException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    compileSdkVersion 29

    lintOptions {
        disable 'InvalidPackage'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.cluxid.m"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            useProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'com.google.guava:guava:27.0.1-android'
    implementation 'com.google.firebase:firebase-messaging:20.2.4'
}

apply plugin: 'com.google.gms.google-services'

Dart code

@override
  Future<User> signInWithFacebook() async {
    final result = await FacebookAuth.instance
        .login(loginBehavior: LoginBehavior.nativeWithFallback);

    switch (result.status) {
      case LoginStatus.success:
        AuthCredential credential =
            FacebookAuthProvider.credential(result.accessToken.token);

        await firebaseAuth.signInWithCredential(credential);
        return firebaseAuth.currentUser;
        break;
      case LoginStatus.cancelled:
        // TODO: Handle this case.
        break;
      case LoginStatus.failed:
        // TODO: Handle this case.
        break;
      case LoginStatus.operationInProgress:
        // TODO: Handle this case.
        break;
    }

    return null;
  }
seifkh97 commented 2 years ago

Now it works fine for me I add a release hash key and my problem was that Facebook doesn't return the email parameter always for all users so it crashes on value["email"] adding ( value["email"] ?? value["id"]+"@facebook.com" ) solve my problem and it's working fine now I tested it on oppo Xiaomi and infinix and it's working hope this helps you

darwin-morocho commented 2 years ago

@darwin-morocho

Manifest looks like this , though i have tried commenting customtab acitivity still no luck


<application android:name=".Application" android:icon="@mipmap/launcher_icon" android:roundIcon="@mipmap/ic_launcher_round" android:label="Mohalla" android:usesCleartextTraffic="true">
      <activity android:name=".MainActivity" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:windowSoftInputMode="adjustResize">
          <!--
          This keeps the window background of the activity showing
          until Flutter renders its first frame. It can be removed if
          there is no splash screen (such as the default splash screen
          defined in @style/LaunchTheme).
          -->
          <!-- <meta-data android:name="io.flutter.app.android.SplashScreenUntilFirstFrame" android:value="true" /> -->
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
          <intent-filter>
              <action android:name="FLUTTER_NOTIFICATION_CLICK" />
              <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
          <intent-filter>
              <action android:name="android.intent.action.SEND" />
              <category android:name="android.intent.category.DEFAULT" />
              <data android:mimeType="text/plain" />
          </intent-filter>
      </activity>

<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>

 <activity android:name="com.facebook.FacebookActivity"
     android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
     android:label="@string/app_name" />
 <activity
     android:name="com.facebook.CustomTabActivity"
     android:exported="true">
     <intent-filter>
         <action android:name="android.intent.action.VIEW" />
         <category android:name="android.intent.category.DEFAULT" />
         <category android:name="android.intent.category.BROWSABLE" />
         <data android:scheme="@string/fb_login_protocol_scheme" />
     </intent-filter>
 </activity>

MainActivity

import android.content.Intent;
import io.flutter.embedding.engine.FlutterEngine;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterFragmentActivity;
import java.nio.ByteBuffer;
import io.flutter.plugin.common.ActivityLifecycleListener;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterFragmentActivity {

  private String sharedText;

  @Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    // GeneratedPluginRegistrant.registerWith(flutterEngine);
    super.configureFlutterEngine(flutterEngine);
    handleSendIntent(getIntent());

    new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "app.channel.shared.data").setMethodCallHandler(new MethodCallHandler() {
      @Override
      public void onMethodCall(MethodCall call, MethodChannel.Result result) {

        if (call.method.contentEquals("getSharedText")) {
          result.success(sharedText);
          sharedText = null;
        }
      }
    });

  }

  @Override
  protected void onNewIntent(Intent intent) {
    // Handle intent when app is resumed
    super.onNewIntent(intent);
    handleSendIntent(intent);
  }

  private void handleSendIntent(Intent intent){

    String action = intent.getAction();
    String type = intent.getType();

    if (Intent.ACTION_SEND.equals(action) && type != null) {
      if ("text/plain".equals(type)) {
        sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); // Handle text being sent
      }
    }
    }

}

Build.gradle

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new FileNotFoundException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    compileSdkVersion 29

    lintOptions {
        disable 'InvalidPackage'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.cluxid.m"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            useProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'com.google.guava:guava:27.0.1-android'
    implementation 'com.google.firebase:firebase-messaging:20.2.4'
}

apply plugin: 'com.google.gms.google-services'

Dart code

@override
 Future<User> signInWithFacebook() async {
   final result = await FacebookAuth.instance
       .login(loginBehavior: LoginBehavior.nativeWithFallback);

   switch (result.status) {
     case LoginStatus.success:
       AuthCredential credential =
           FacebookAuthProvider.credential(result.accessToken.token);

       await firebaseAuth.signInWithCredential(credential);
       return firebaseAuth.currentUser;
       break;
     case LoginStatus.cancelled:
       // TODO: Handle this case.
       break;
     case LoginStatus.failed:
       // TODO: Handle this case.
       break;
     case LoginStatus.operationInProgress:
       // TODO: Handle this case.
       break;
   }

   return null;
 }

You have 2 problems. First you have firebase messaging in your build.gradle that is not need it because that dependency is included in firebase_messagin. the second problem is your MainActivity extends of flutterfragmentactivity so you need to override the oncreate method an call to super.onCreate to keep the app activity state after resume

here one example https://pub.dev/packages/local_auth

itssidhere commented 2 years ago

@darwin-morocho onCreate method is deprecated isn't it?

darwin-morocho commented 2 years ago

@darwin-morocho onCreate method is deprecated isn't it?

no. is not deprecated

darwin-morocho commented 2 years ago

Now it works fine for me I add a release hash key and my problem was that Facebook doesn't return the email parameter always for all users so it crashes on value["email"] adding ( value["email"] ?? value["id"]+"@facebook.com" ) solve my problem and it's working fine now I tested it on oppo Xiaomi and infinix and it's working hope this helps you

Have you tested in device with Android 11?

itssidhere commented 2 years ago

@darwin-morocho https://api.flutter.dev/javadoc/io/flutter/app/FlutterFragmentActivity.html#onCreate-android.os.Bundle-

darwin-morocho commented 2 years ago

https://api.flutter.dev/javadoc/io/flutter/app/FlutterFragmentActivity.html#onCreate-android.os.Bundle-

maybe is deprecated but what you are using FlutterFragmentActivity?

itssidhere commented 2 years ago

@darwin-morocho for local auth

darwin-morocho commented 2 years ago

@darwin-morocho for local auth

if you are using java according to local auth documentation you need onCreate

itssidhere commented 2 years ago

Actually, their documentation was outdated I had a hard time with that too. Let me add onCreate and update you??

darwin-morocho commented 2 years ago

Actually, their documentation was outdated I had a hard time with that too. Let me add onCreate and update you??

yes use onCreate please or try to use FlutterActivity and test the. login with facebook if the login works with FlutterActivity you need to check your MainActivity and this is not a bug of this plugin and this issue should be closed

itssidhere commented 2 years ago

I added this line

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

I do agree with you that this is an issue of the underlying plugin but it should be kept open to discuss the workaround as more people encounter it they will create separate issues. Maybe we can come to a solution here.

darwin-morocho commented 2 years ago

I added this line

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

I do agree with you that this is an issue of the underlying plugin but it should be kept open to discuss the workaround as more people encounter it they will create separate issues. Maybe we can come to a solution here.

if the problem was fixed with onCreate method I will add this to the documentation

itssidhere commented 2 years ago

@darwin-morocho I tested it and it's the same. I am trying to pass null to the super method call of onCreate as suggested by the react-native solution. I will update you

itssidhere commented 2 years ago

@darwin-morocho still no luck

darwin-morocho commented 2 years ago

@darwin-morocho still no luck

Using the FlutterActivity instead of FlutterFragmentActivity?

itssidhere commented 2 years ago

@darwin-morocho still same

darwin-morocho commented 2 years ago

@darwin-morocho still same

I have tried to reproduce the issue installing local_auth and running the app in release mode with a HUAWEI with android 10 and everything is ok

darwin-morocho commented 2 years ago

please try this apk and let me know if the app crashes

app-release.apk.zip

itssidhere commented 2 years ago

Your app is working it seems

WhatsApp Image 2021-08-24 at 6 50 06 PM

itssidhere commented 2 years ago

@darwin-morocho How did you manage to fix it?

darwin-morocho commented 2 years ago

nothing special just I have added the release key hash in the Facebook developers console and I have changed the app to production in the console . And also to generate this hash I used mac os

itssidhere commented 2 years ago

My app is signed by google play store so I had to manually convert the sha1 to base64 . It works on every device except mine. But your app works on mine too

darwin-morocho commented 2 years ago

My app is signed by google play store so I had to manually convert the sha1 to base64 . It works on every device except mine. But your app works on mine too

this app uses flutter Facebook auth https://play.google.com/store/apps/details?id=app.meedu.app can you install it and check if the problem persists

itssidhere commented 2 years ago

@darwin-morocho This app also works fine in my phone

darwin-morocho commented 2 years ago

@darwin-morocho This app also works fine in my phone

Google play console creates 2 sha1. Have you added both in your Facebook developers console?

itssidhere commented 2 years ago

@darwin-morocho yes

darwin-morocho commented 2 years ago

@darwin-morocho yes

in that case maybe your MainActivity is locking the Facebook sdk

itssidhere commented 2 years ago

Can you send me your MainActivity so that I can try to replicate it. I have also overriden Application java from mainfest can that be an issue as well

darwin-morocho commented 2 years ago

Can you send me your MainActivity so that I can try to replicate it. I have also overriden Application java from mainfest can that be an issue as well

just check the example folder of this repo

itssidhere commented 2 years ago

Still no luck

darwin-morocho commented 2 years ago

Still no luck

Have you tried creating a release keystore and creating the hash and next create a release apk using this keystore? also you need to add this hash in the facebook developers console

itssidhere commented 2 years ago

@darwin-morocho its working now with that method!

darwin-morocho commented 2 years ago

@darwin-morocho its working now with that method!

it seems a problem with your hash from google play console

itssidhere commented 2 years ago

Then why is it working on some devices but not all? Also can you tell me how you created the key from google play console sha1?