Flutter:在 Firebase 中有不同的身份验证提供程序

Flutter: have different authentication providers in Firebase

我的应用程序中有不同的登录方法:

对于这个问题,我将重点关注前两个问题。当用户使用 Facebook 登录时,提供商看起来像这样:

没关系,但如果我注销并再次登录,这次使用新的 Google 帐户但使用相同的电子邮件,提供商将如下所示:

现在,如果我注销并再次使用 Facebook 登录,我会遇到 account-exists-with-different-credential 错误。我已经准备好逻辑并显示其提供者登录方法的东西,但是这个用户应该有两个可用的提供者并且他应该能够使用这两种方法登录。

这是我的代码:

Future facebookSignIn(BuildContext context) async {
    final LoginResult result = await FacebookAuth.instance.login();

    if (result.status == LoginStatus.success) {
      final AccessToken accessToken = result.accessToken!;
      AuthCredential credential =
          FacebookAuthProvider.credential(accessToken.token);
      await _firebaseCredential(context, credential);
    }
  }

 Future googleSignIn(BuildContext context,
      [String? email, facebookCredential]) async {
    try {
      GoogleSignInAccount googleUser;
      dynamic popup = await _googleSignIn.signIn();

      // cancelled login
      if (popup == null) {
        return null;
      }

      googleUser = popup;

      GoogleSignInAuthentication googleAuth = await googleUser.authentication;
      final AuthCredential credential = GoogleAuthProvider.credential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );
      await _firebaseCredential(context, credential);
    } on FirebaseAuthException catch (e) {
      // await FirebaseCrashlytics.instance.recordError(
      //   e,
      //   StackTrace.fromString("/googleSignIn"),
      //   reason: e.message,
      // );
      // return null;
    }
  }

 _firebaseCredential(BuildContext context, credential) async {
    try {
      User user =
          (await FirebaseAuth.instance.signInWithCredential(credential)).user!;
      // Provider.of<MyRents>(context, listen: false).updateUI();
      await firebaseProfile.updateUserData(context, user);
    } on FirebaseAuthException catch (error) {
      // final error = e as FirebaseAuthException;
      if (error.code == 'account-exists-with-different-credential') {
        String email = error.email!;
        // AuthCredential pendingCredential = e.credential;

        List<String> signInMethods =
            await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);

        // If the user has several sign-in methods,
        // the first method in the list will be the "recommended" method to use.
        if (signInMethods.first == 'google.com' ||
            signInMethods.first == 'facebook.com') {
          // TODO: fix facebook
          return await googleSignIn(context, email, credential);
        } else {
          ScaffoldMessenger.of(context)
              .showSnackBar(SnackBar(content: Text(error.message!)));
        }
      } else {
        ScaffoldMessenger.of(context)
            .showSnackBar(SnackBar(content: Text(error.message!)));
      }
    }
  }

我是不是漏掉了什么?

flutter_facebook_auth: ^4.3.3
google_sign_in: ^5.2.1
Future googleSignIn(BuildContext context,
      [String? email, facebookCredential]) async {
    try {
      GoogleSignInAccount googleUser;
      dynamic popup = await _googleSignIn.signIn();

      // cancelled login
      if (popup == null) {
        return null;
      }

      googleUser = popup;

      GoogleSignInAuthentication googleAuth = await googleUser.authentication;
      final AuthCredential credential = GoogleAuthProvider.credential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );
      await _firebaseCredential(context, credential);
    } on FirebaseAuthException catch (e) {
      // await FirebaseCrashlytics.instance.recordError(
      //   e,
      //   StackTrace.fromString("/googleSignIn"),
      //   reason: e.message,
      // );
      // return null;
    }
  }

Future facebookSignIn(BuildContext context) async {
    final LoginResult result = await FacebookAuth.instance.login();

    if (result.status == LoginStatus.success) {
      final AccessToken accessToken = result.accessToken!;
      AuthCredential credential =
          FacebookAuthProvider.credential(accessToken.token);

      await _firebaseCredential(context, credential);
    }
  }

// other methods...

_firebaseCredential(BuildContext context, credential) async {
    try {
      User user =
          (await FirebaseAuth.instance.signInWithCredential(credential)).user!;
      await firebaseProfile.updateUserData(context, user);
    } on FirebaseAuthException catch (error) {
      if (error.code == 'account-exists-with-different-credential') {
        String email = error.email!;
        List<String> signInMethods =
            await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
        // bool newUser = (signInMethods.length > 0) ? false : true;

        // If the user has several sign-in methods,
        // the first method in the list will be the "recommended" method to use.
        var user;
        switch (signInMethods.first) {
          case 'google.com':
            user = await googleSignIn(context, email, credential);
            break;
          case 'facebook.com':
            user = await facebookSignIn(context);
            break;
          case 'apple.com':
            user = await appleSignIn(context);
            break;
          case 'password':
            // since password is managed by user we force have email provider only
            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                content: Text(translate('auth.signInMethods_password'))));
            break;
          // TODO: apple
        }
        await linkProvider(context, credential);
        return user;
      }

      return ScaffoldMessenger.of(context)
          .showSnackBar(SnackBar(content: Text(error.message!)));
    }
  }

  // just some extra error covering
  Future linkProvider(BuildContext context, credential) async {
    try {
      await FirebaseAuth.instance.currentUser?.linkWithCredential(credential);
    } on FirebaseAuthException catch (e) {
      switch (e.code) {
        case "provider-already-linked":
          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
              content: Text(translate('auth.provider_already_linked'))));
          break;
        case "invalid-credential":
          ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text(translate('auth.invalid_credential'))));
          break;
        case "credential-already-in-use":
          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
              content: Text(translate('auth.credential_already_in_use'))));
          break;
        default:
          ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text(translate('auth.something_happened'))));
      }
    }
  }

如果你这样做 Google -> Facebook 它将看起来像这样:

如果您的 Google 电子邮件是受信任的电子邮件 (gmail),则只有 Google 会出现。更多信息: https://groups.google.com/g/firebase-talk/c/ms_NVQem_Cw/m/8g7BFk1IAAAJ