Ad

Flutter Web: Safari Google Sign In First Attempt Blocked

I am developing a Flutter Web application that uses Google Sign In alongside Firebase Authentication. Currently, Google Sign In operates perfectly normal on most browsers. I noticed, though, that while using Safari on iPhone with popups blocked, the first authentication fails, it doesn't work (nothing comes up). Seemingly, the popup is blocked. By tapping the button immediately after, though, the authentication succeeds.

The first interaction in Safari simply does not work, even though it will in Firefox or Chrome. I have also noticed the Google Sign In method not working in private (incognito) browsers.

Here is my current googleSignIn() method:

static Future<void> signInWithGoogle() async {
try {
  final GoogleSignIn googleSignIn = GoogleSignIn();

  final GoogleSignInAccount? googleSignInAccount =
      await googleSignIn.signIn();

  if (googleSignInAccount != null) {
    final GoogleSignInAuthentication googleSignInAuthentication =
        await googleSignInAccount.authentication;

    final AuthCredential credential = GoogleAuthProvider.credential(
      accessToken: googleSignInAuthentication.accessToken,
      idToken: googleSignInAuthentication.idToken,
    );

    await FirebaseAuth.instance.signInWithCredential(credential);
  }
} catch (error) {
  throw error;
}

}

As stated, it works fine in all the other browsers that I have tested. It also works in Safari when popups are not blocked, although upon the first call it asks if the user wants to allow the popup.

I would like to note that other websites that use Google Sign In do not run into this same issue, it does seem to be specific to Flutter's Google Sign In package.

Ad

Answer

After scouring GitHub and StackOverflow, I found the solution. In the initState of the initial screen of your app, call GoogleSignIn().signInSilently(). This is not Google's intentional functionality, but rather a pleasant workaround to a bug.

See Google's own google_sign_in example for more context. Be sure to focus on initState.

Lastly, if you're using Firebase Authentication along with Google Sign In, here's the method to capture the user object that silentSignIn() produces so you can reap the benefits.

static Future<void> signInWithGoogleSilently() async {
  try {
    final GoogleSignIn googleSignIn = GoogleSignIn();

    final GoogleSignInAccount? googleSignInAccount =
        await googleSignIn.signInSilently();

    if (googleSignInAccount != null) {
      final GoogleSignInAuthentication googleSignInAuthentication =
          await googleSignInAccount.authentication;

      final AuthCredential credential = GoogleAuthProvider.credential(
        accessToken: googleSignInAuthentication.accessToken,
        idToken: googleSignInAuthentication.idToken,
      );

      await FirebaseAuth.instance.signInWithCredential(credential);
    }
  } catch (error) {
    throw error;
  }
}
Ad
source: stackoverflow.com
Ad