Flutterfire: [firebase_auth] EmailwithSignInLink never consistent and keeps throwing ERROR_INVALID_ACTION_CODE

Created on 3 Dec 2019  Â·  14Comments  Â·  Source: FirebaseExtended/flutterfire

Describe the bug
Users are not able to signIn to the app using emailLink.
The first email link for that day works. Then all the other signups fail with the error.
PlatformException(ERROR_INVALID_ACTION_CODE, The action code is invalid. This can happen if the code is malformed, expired, or has already been used., null)

To Reproduce

  1. You can use the same code as sample example.
  2. Have all the configurations set up and dynamicsLink setup and domain whitelisting etc.
  3. Send sendSignInWithEmailLink.
  4. The link comes and the user is able to signIn.
  5. Create more users and email arrives, but none of them are able to signIn.

Expected behavior
Consistent behaviour expected. Same Link and same actioncode settings are sent. one time the user is able to signIn the other time they are not able to.

Additional context
My sendSignInWithEmailLink code
Future<bool> signInWithEmailAndLink(String _userEmail) async { try { await auth.sendSignInWithEmailLink( email: _userEmail, url: 'https://xxxxxxxxxxxxx.page.link/?email=$_userEmail', //This domain is whitelisted. handleCodeInApp: true, iOSBundleID: 'dk.xxxxx.yyyyyyy.qa', androidPackageName: 'dk.xxxxx.yyyyyyy.qa', androidInstallIfNotAvailable: true, androidMinimumVersion: "16", ); return true; } catch (err) { print(err); return false; } }
My signIn
Future<AuthResult> signIn(String email, String link) async { return await auth.signInWithEmailAndLink(email: email, link: link); }

Flutter Doctor

✓] Flutter (Channel dev, v1.12.6, on Mac OS X 10.14.6 18G103, locale en-DK)
[!] Android toolchain - develop for Android devices (Android SDK version 29.0.1)
! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
[✓] Xcode - develop for iOS and macOS (Xcode 10.3)
[✓] Android Studio (version 3.5)
[✓] VS Code (version 1.40.1)
[✓] Connected device (1 available)

pubspec file

firebase_auth: ^0.14.0+8
firebase_dynamic_links: ^0.5.0+1

customer-response customer auth needs-repro

Most helpful comment

Same thing here. Still no solution?

I found one solution, the problem is not with the code, its with google play services library. once you clear cache form setting for the google play services library, it will start working. Ggl play service library is caching the url, sometimes it returns old code. Try clearing the cache form setting it will work.
Hope this will help you

All 14 comments

@shriharip Hi did you got the solution for this issue. ?

No I could not find a solution

Same issue i am also facing, magic links are consistantly failing.

I'm having this same problem. Nothing yet?

Same thing here. Still no solution?

Same thing here. Still no solution?

I found one solution, the problem is not with the code, its with google play services library. once you clear cache form setting for the google play services library, it will start working. Ggl play service library is caching the url, sometimes it returns old code. Try clearing the cache form setting it will work.
Hope this will help you

@lsurage any idea on how to do the same on iOS? I'm having a similar problem with iOS, although I can't reproduce it and catch the error. I'm just getting angry users reviews although it works for most people. But this cache problem makes sense and I'd bet on that.

I am facing the same issue in android. For now, I think the following workaround should work.

try {
 await FirebaseAuth.instance.signInWithEmailAndLink(email: _email, link: _link);
} catch (e) { 
 //Assuming it would take atmost about half a second for system to log the user in, given that the link was actually appropriate one. 
 await Future.delayed(const Duration(milliseconds: 500), (){});
 FirebaseUser user = await FirebaseAuth.instance.currentUser();
 if(user==null) {
   PlatformException err = e;
   if (err.code == "ERROR_INVALID_ACTION_CODE") { 
        //TODO Handle Malformed or Expired login link
   }
 }
}

Note that, In my case every user gets logged in successfully, just some of them get the dialog about the expired link, for a moment before the app main page is opened.

I think the problem is because you might be using getInitialLink only.

In older versions of firebase auth you made an override of didChangeAppLifecycleState and then used retrieveDynamicLink to get the link. I have seen a couple of developers when upgrading to newer version of firebase auth, they simply replace retrieveDynamicLink with getInitialLink, however this is not enough, since getInitialLink only returns something other than null, the first time you call it (initial, i.e. supposed to be on app start).

Instead you should follow the example given in Firebase dynamic links, see initDynamicLinks.

I have modified it a bit, but here is how I handle dynamic links:

void initDynamicLinks() async {
    final initialLinkData = await FirebaseDynamicLinks.instance.getInitialLink();
    if (initialLinkData != null) {
      print ('handling initialLinkData');
      await _handleLink(initialLinkData);
    }
    FirebaseDynamicLinks.instance.onLink(
        onSuccess: (PendingDynamicLinkData linkData) async {
          print ('handling linkData');
          await _handleLink(linkData);
        },
        onError: (OnLinkErrorException e) async {
          print('onLinkError: ${e.message}');
        }
    );
  }

  Future<void> _handleLink(PendingDynamicLinkData linkData) async {
    if (linkData == null) {
      return;
    }

    final deepLink = linkData.link;
    if (deepLink == null) {
      return;
    }

    // ..... some other code I use to verify the links minimumVersion against the current app version.

    // Handle the deep links supported on this version of the app.
    await _handleValidDeepLink(deepLink);
  }

  Future<void> _handleValidDeepLink(Uri deepLink) async {
    final authBloc = Provider.of<AuthenticationBloc>(context);
    if (await authBloc.isSignInWithEmailLink(deepLink)) {
      await authBloc.handleSignInWithEmailLinkReceived(deepLink);
    } else {
      // TODO handle other type of deep links.
      //Navigator.pushNamed(context, deepLink.path);
    }
  }

I hope this is the case for you guys as well, so you can return to a stable login experience for the users. :)

@janniklind, Thank you for your detailed comment.
I was already handling the links as per your format, and the issue still happens.

@janniklind I'm also doing the same way as you explained. Faced the issue you mentioned in the past though, but it just din't work. Now it works most of the times, but not always. I can't even reproduce it on my devices.

My app close on call: result = await _firebaseAuth.signInWithEmailAndLink(email: usuario.email, link: 'https://exemple.com');

Throw Exception:
Lost connection to device. *** First throw call stack: ( 0 CoreFoundation 0x00007fff23c4f02e __exceptionPreprocess + 350 1 libobjc.A.dylib 0x00007fff50b97b20 objc_exception_throw + 48 2 CoreFoundation 0x00007fff23c4ee6c +[NSException raise:format:] + 188 3 Runner 0x000000010b1c3b38 +[FIRAuthExceptionUtils raiseInvalidParameterExceptionWithReason:] + 104 4 Runner 0x000000010b1a7775 -[FIRAuth internalSignInAndRetrieveDataWithEmail:link:callback:] + 181 5 Runner 0x000000010b1a856e -[FIRAuth internalSignInAndRetrieveDataWithCredential:isReauthentication:callback:] + 382 6 Runner <…>

Check if https://xxxxxxxxxxxxx.page.link is default link on dynamic link section. I had a chunk link which was showing as default that caused me the problem. Hopefully this helps you :)

Hey 👋

Our rework of the firebase_auth plugin as part of the FlutterFire roadmap was published over a week ago with a ton of fixes and new features. Please could you try the new version and see if this is still an issue for you? If it is then please submit a new up to date GitHub issue.

For help migrating to the new plugins please see the new migration guide: https://firebase.flutter.dev/docs/migration

Was this page helpful?
0 / 5 - 0 ratings