firebase / flutterfire

🔥 A collection of Firebase plugins for Flutter apps.
https://firebase.google.com/docs/flutter/setup
BSD 3-Clause "New" or "Revised" License
8.7k stars 3.97k forks source link

🐛 [firebase_auth] signInWithCredential after attempted linking results in session-expired #6006

Closed MrAlek closed 3 years ago

MrAlek commented 3 years ago

Bug report

We're using phone auth to verify the identity of anonymous users.

According to documentation, we should first try using linkWithCredential on the current anonymous user, and then fall back to signing in on credential-already-in-use if that phone number is already in use.

When we do this, the latter signInWIthCredential fails with a PlatformException(session-expired) error.

Disclaimer! Only reproducable with real phone numbers (not test phones in the firebase console).

Steps to reproduce

  1. Log in as an anonymous user using FirebaseAuth.instance.signInAnonymously()
  2. Perform phone verification with FirebaseAuth.instance.verifyPhoneNumber() for a real phone number which is already used by another user.
  3. Try linking that phone number to the current anonymous auth with FirebaseAuth.instance.currentUser.linkWithCredential(phoneCredential);
  4. Catch credential-already-in-use error and now, sign in as that user instead with FirebaseAuth.instance.signInWithCredential(phoneCredential);
  5. Receive this error:
    PlatformException (PlatformException(session-expired, The SMS code has expired. Please re-send the verification code to try again., {code: session-expired, message: The SMS code has expired. Please re-send the verification code to try again., nativeErrorMessage: The SMS code has expired. Please re-send the verification code to try again., nativeErrorCode: 17051, additionalData: {}}, null))

Our code:

try {
  await FirebaseAuth.instance.currentUser.linkWithCredential(phoneCredential);
} on FirebaseAuthException catch (exception) {
  if (exception.code == 'credential-already-in-use') {
    // There's already another user with this phone number. Log in as that user instead.
    await FirebaseAuth.instance.signInWithCredential(phoneCredential);
  } else {
    rethrow;
  }
}

Expected behavior

Expected (and working on Android, and with test phone numbers), is that the signInWithCredential call succeeds and signs the user in.


Additional context

I haven't dug deep into the implementation but what's weird is that the error received is a PlatformException and not a FirebaseAuthException. ~This leads to something in the iOS plugin bridge code.~

From documentation:

  /// - **credential-already-in-use**:
  ///  - Thrown if the account corresponding to the credential already exists
  ///    among your users, or is already linked to a Firebase User. For example,
  ///    this error could be thrown if you are upgrading an anonymous user to a
  ///    Google user by linking a Google credential to it and the Google
  ///    credential used is already associated with an existing Firebase Google
  ///    user. The fields `email`, `phoneNumber`, and `credential`
  ///    ([AuthCredential]) may be provided, depending on the type of
  ///    credential. You can recover from this error by signing in with
  ///    `credential` directly via [signInWithCredential].

The workaround is to go straight to signInWithCredentials without trying to link to the current anonymous user. But this creates unnecessary new user objects in the database and makes state management harder since the phone verification essentially becomes a sign out + sign in event.


Flutter doctor

Run flutter doctor and paste the output below:

Click To Expand ``` Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 2.0.5, on macOS 11.2.3 20D91 darwin-x64, locale sv) [✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2) [✓] Xcode - develop for iOS and macOS [✓] Chrome - develop for the web [✓] Android Studio (version 4.1) [✓] VS Code (version 1.55.2) [✓] Connected device (3 available) • No issues found! ```

Flutter dependencies

Run flutter pub deps -- --style=compact and paste the output below:

Click To Expand ``` Dart SDK 2.12.3 Flutter SDK 2.0.5 done_flutter 1.27.2+141 dependencies: - app_review 2.1.1+1 [http package_info url_launcher flutter] - app_settings 4.0.3 [flutter] - audioplayers 0.18.3 [uuid path_provider flutter flutter_web_plugins] - auto_size_text 2.1.0 [flutter] - cached_network_image 2.5.1 [flutter flutter_cache_manager octo_image] - camera 0.5.8+17 [flutter] - chewie 1.0.0 [cupertino_icons flutter video_player wakelock] - cloud_firestore 1.0.6 [cloud_firestore_platform_interface cloud_firestore_web firebase_core firebase_core_platform_interface flutter meta] - cloud_functions 1.0.4 [cloud_functions_platform_interface cloud_functions_web firebase_core firebase_core_platform_interface flutter] - confetti 0.5.5 [flutter random_color vector_math] - connectivity 3.0.3 [flutter meta connectivity_platform_interface connectivity_macos connectivity_for_web] - cupertino_icons 1.0.2 - device_calendar 3.1.0 [flutter meta collection] - device_info_plus 1.0.0 [flutter device_info_plus_platform_interface device_info_plus_linux device_info_plus_macos device_info_plus_web device_info_plus_windows] - done_ui 0.0.1 [flutter] - event_bus 1.1.1 - expandable 4.1.4 [flutter] - expandable_bottom_sheet 0.2.1+1 [flutter] - facebook_app_events 0.11.2 [flutter] - file_picker 3.0.1 [flutter flutter_web_plugins flutter_plugin_android_lifecycle plugin_platform_interface] - firebase_analytics 8.0.1 [firebase_analytics_platform_interface firebase_analytics_web firebase_core flutter meta] - firebase_auth 1.1.2 [firebase_auth_platform_interface firebase_auth_web firebase_core firebase_core_platform_interface flutter meta] - firebase_core 1.1.0 [firebase_core_platform_interface firebase_core_web flutter meta] - firebase_crashlytics 2.0.1 [firebase_core firebase_core_platform_interface firebase_crashlytics_platform_interface flutter stack_trace] - firebase_dynamic_links 2.0.1 [firebase_core flutter] - firebase_messaging 9.1.2 [firebase_core firebase_core_platform_interface firebase_messaging_platform_interface firebase_messaging_web flutter meta] - firebase_storage 8.0.4 [firebase_core firebase_core_platform_interface firebase_storage_platform_interface firebase_storage_web flutter] - flame 0.29.4 [flutter audioplayers ordered_set box2d_flame synchronized convert flare_flutter meta] - flutter 0.0.0 [characters collection meta typed_data vector_math sky_engine] - flutter_app_badger 1.2.0 [flutter] - flutter_callkeep 0.1.8 [flutter] - flutter_document_picker 4.0.1 [flutter] - flutter_incall_manager 0.1.0 [flutter] - flutter_linkify 3.1.3 [flutter linkify] - flutter_localizations 0.0.0 [flutter intl characters clock collection meta path typed_data vector_math] - flutter_markdown 0.4.4 [flutter markdown meta path] - flutter_native_image 0.0.6 [flutter] - flutter_observable 0.2.0 [flutter] - flutter_pdfview 1.1.0 [flutter] - flutter_sticky_header 0.5.0 [flutter value_layout_builder] - flutter_webrtc 0.4.1 [flutter] - gen_lang 0.1.3 [args path ansicolor] - geocoder 0.2.1 [meta flutter] - get_it 4.0.4 [async meta] - google_maps_flutter 0.5.21+17 [flutter] - google_maps_webservice 0.0.20-nullsafety.4 [http meta json_annotation] - image_downloader 0.20.1 [flutter] - image_picker 0.7.4 [flutter flutter_plugin_android_lifecycle image_picker_platform_interface image_picker_for_web] - intercom_flutter 3.1.0 [flutter] - intl 0.17.0 [clock path] - libphonenumber 1.0.2 [flutter meta] - logger 0.9.4 - modal_bottom_sheet 2.0.0-nullsafety.1 [flutter] - package_info_plus 1.0.0 [flutter package_info_plus_platform_interface package_info_plus_linux package_info_plus_macos package_info_plus_windows package_info_plus_web] - page_view_indicators 1.4.1 [flutter] - path 1.8.0 - path_provider 2.0.1 [flutter path_provider_platform_interface path_provider_macos path_provider_linux path_provider_windows] - permission_handler 6.1.3 [flutter meta permission_handler_platform_interface] - personnummer 3.0.3 - photo_view 0.10.3 [flutter] - pin_code_fields 7.0.0 [flutter] - pointer_interceptor 0.9.0 [flutter] - pub_semver 1.4.4 [collection] - rxdart 0.24.1 - share 2.0.1 [meta mime flutter] - shared_preferences 2.0.5 [meta flutter shared_preferences_platform_interface shared_preferences_linux shared_preferences_macos shared_preferences_web shared_preferences_windows] - smooth_star_rating 1.1.1 [flutter] - sprung 3.0.0 [flutter] - stream_transform 1.2.0 - string_validator 0.1.4 - uni_links2 0.6.0+2 [uni_links2_platform_interface uni_links_web2 flutter] - universal_html 2.0.8 [async csslib charcode collection html meta source_span typed_data universal_io] - universal_platform 0.1.3 - universal_ui 0.0.8 [flutter] - url_launcher 6.0.3 [flutter url_launcher_platform_interface url_launcher_linux url_launcher_macos url_launcher_windows url_launcher_web] - uuid 3.0.4 [crypto] - video_compress 2.1.1 [flutter] - video_player 2.1.1 [meta video_player_platform_interface video_player_web flutter flutter_test] dev dependencies: - flutter_driver 0.0.0 [file meta path vm_service webdriver flutter flutter_test fuchsia_remote_debug_protocol archive args async boolean_selector characters charcode clock collection convert crypto fake_async matcher platform process source_span stack_trace stream_channel string_scanner sync_http term_glyph test_api typed_data vector_math] - flutter_launcher_icons 0.8.1 [image args yaml path] - flutter_test 0.0.0 [flutter test_api path fake_async clock stack_trace vector_math async boolean_selector characters charcode collection matcher meta source_span stream_channel string_scanner term_glyph typed_data] - test 1.16.5 [analyzer async boolean_selector coverage http_multi_server io js node_preamble package_config path pedantic pool shelf shelf_packages_handler shelf_static shelf_web_socket source_span stack_trace stream_channel typed_data web_socket_channel webkit_inspection_protocol yaml test_api test_core] - very_good_analysis 2.0.3 [pedantic] dependency overrides: - crypto 2.1.5 [collection convert typed_data] - http 0.13.1 [http_parser meta path pedantic] - lang_table 0.2.0 [args path ansicolor http] transitive dependencies: - _fe_analyzer_shared 14.0.0 [meta] - analyzer 0.41.2 [_fe_analyzer_shared args cli_util collection convert crypto glob meta package_config path pub_semver source_span watcher yaml] - ansicolor 1.1.1 - archive 2.0.13 [crypto args path] - args 1.6.0 - async 2.5.0 [collection] - boolean_selector 2.1.0 [source_span string_scanner] - box2d_flame 0.4.6 [vector_math] - characters 1.1.0 - charcode 1.2.0 - cli_util 0.3.0 [meta path] - clock 1.1.0 - cloud_firestore_platform_interface 4.0.2 [collection firebase_core flutter meta plugin_platform_interface] - cloud_firestore_web 1.0.6 [cloud_firestore_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins js] - cloud_functions_platform_interface 5.0.2 [firebase_core flutter meta plugin_platform_interface] - cloud_functions_web 4.0.4 [cloud_functions_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins js] - collection 1.15.0 - connectivity_for_web 0.4.0 [connectivity_platform_interface flutter_web_plugins flutter] - connectivity_macos 0.2.0 [flutter] - connectivity_platform_interface 2.0.1 [flutter meta plugin_platform_interface] - convert 2.1.1 [charcode typed_data] - coverage 0.15.2 [args logging package_config path source_maps stack_trace vm_service] - csslib 0.17.0 [source_span] - device_info_plus_linux 1.0.0 [device_info_plus_platform_interface file flutter meta] - device_info_plus_macos 1.0.0 [device_info_plus_platform_interface flutter] - device_info_plus_platform_interface 1.0.1 [flutter meta plugin_platform_interface] - device_info_plus_web 1.0.0 [device_info_plus_platform_interface flutter_web_plugins flutter] - device_info_plus_windows 1.0.0 [device_info_plus_platform_interface ffi flutter win32] - fake_async 1.2.0 [clock collection] - ffi 1.0.0 - file 6.0.0 [meta path] - firebase 9.0.1 [http http_parser js] - firebase_analytics_platform_interface 2.0.0 [flutter meta] - firebase_analytics_web 0.3.0 [firebase firebase_analytics_platform_interface flutter flutter_web_plugins meta] - firebase_auth_platform_interface 4.2.0 [firebase_core flutter meta plugin_platform_interface] - firebase_auth_web 1.1.0 [firebase_auth_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins http_parser intl js meta] - firebase_core_platform_interface 4.0.0 [flutter meta plugin_platform_interface] - firebase_core_web 1.0.2 [firebase_core_platform_interface flutter flutter_web_plugins js meta] - firebase_crashlytics_platform_interface 3.0.1 [collection firebase_core flutter meta plugin_platform_interface] - firebase_messaging_platform_interface 2.1.2 [firebase_core flutter meta plugin_platform_interface] - firebase_messaging_web 1.0.5 [firebase_core firebase_core_web firebase_messaging_platform_interface flutter flutter_web_plugins js meta] - firebase_storage_platform_interface 2.0.2 [collection firebase_core flutter meta plugin_platform_interface] - firebase_storage_web 1.0.4 [async firebase_core firebase_core_web firebase_storage_platform_interface flutter flutter_web_plugins http js meta] - flare_dart 2.3.4 - flare_flutter 2.0.6 [flutter flare_dart meta] - flutter_blurhash 0.5.0 [flutter meta] - flutter_cache_manager 2.1.2 [flutter path_provider uuid http path sqflite pedantic clock file rxdart image] - flutter_plugin_android_lifecycle 2.0.1 [flutter] - flutter_web_plugins 0.0.0 [flutter js characters collection meta typed_data vector_math] - fuchsia_remote_debug_protocol 0.0.0 [process vm_service file meta path platform] - glob 2.0.1 [async collection file path pedantic string_scanner] - html 0.15.0 [csslib source_span] - http_multi_server 3.0.1 [async] - http_parser 4.0.0 [charcode collection source_span string_scanner typed_data] - image 2.1.19 [archive xml meta] - image_picker_for_web 2.0.0 [image_picker_platform_interface meta flutter flutter_web_plugins] - image_picker_platform_interface 2.1.0 [flutter meta http plugin_platform_interface] - io 1.0.0 [meta path string_scanner] - js 0.6.3 - json_annotation 4.0.1 - linkify 2.1.0 - logging 1.0.1 - markdown 2.1.8 [args charcode meta] - matcher 0.12.10 [stack_trace] - meta 1.3.0 - mime 1.0.0 - node_preamble 1.4.13 - octo_image 0.3.0 [flutter flutter_blurhash] - ordered_set 2.0.2 - package_config 1.9.3 [path charcode] - package_info 2.0.0 [flutter] - package_info_plus_linux 1.0.0 [package_info_plus_platform_interface flutter path] - package_info_plus_macos 1.0.0 [flutter] - package_info_plus_platform_interface 1.0.0 [flutter meta plugin_platform_interface] - package_info_plus_web 1.0.0 [http package_info_plus_platform_interface flutter flutter_web_plugins meta] - package_info_plus_windows 1.0.0 [package_info_plus_platform_interface ffi flutter win32] - path_provider_linux 2.0.0 [path xdg_directories path_provider_platform_interface flutter] - path_provider_macos 2.0.0 [flutter] - path_provider_platform_interface 2.0.1 [flutter meta platform plugin_platform_interface] - path_provider_windows 2.0.0 [path_provider_platform_interface meta path flutter ffi win32] - pedantic 1.11.0 - permission_handler_platform_interface 3.2.0 [flutter meta plugin_platform_interface] - petitparser 3.1.0 [meta] - platform 3.0.0 - plugin_platform_interface 2.0.0 [meta] - pool 1.5.0 [async stack_trace] - process 4.0.0 [file path platform] - random_color 1.0.5 [flutter] - shared_preferences_linux 2.0.0 [flutter file meta path path_provider_linux shared_preferences_platform_interface] - shared_preferences_macos 2.0.0 [shared_preferences_platform_interface flutter] - shared_preferences_platform_interface 2.0.0 [flutter] - shared_preferences_web 2.0.0 [shared_preferences_platform_interface flutter flutter_web_plugins meta] - shared_preferences_windows 2.0.0 [shared_preferences_platform_interface flutter file meta path path_provider_platform_interface path_provider_windows] - shelf 1.1.0 [async collection http_parser path stack_trace stream_channel] - shelf_packages_handler 2.0.1 [path shelf shelf_static] - shelf_static 0.2.9+2 [convert http_parser mime path shelf] - shelf_web_socket 1.0.1 [shelf stream_channel web_socket_channel] - sky_engine 0.0.99 - source_map_stack_trace 2.1.0 [path stack_trace source_maps] - source_maps 0.10.10 [source_span] - source_span 1.8.0 [charcode collection path term_glyph] - sqflite 1.3.2+4 [flutter sqflite_common path] - sqflite_common 1.0.3+3 [synchronized path meta] - stack_trace 1.10.0 [path] - stream_channel 2.1.0 [async] - string_scanner 1.1.0 [charcode source_span] - sync_http 0.2.0 - synchronized 2.2.0+2 - term_glyph 1.2.0 - test_api 0.2.19 [async boolean_selector collection meta path source_span stack_trace stream_channel string_scanner term_glyph matcher] - test_core 0.3.15 [analyzer async args boolean_selector collection coverage glob io meta package_config path pedantic pool source_map_stack_trace source_maps source_span stack_trace stream_channel vm_service yaml matcher test_api] - typed_data 1.3.0 [collection] - uni_links2_platform_interface 1.0.0+2 [plugin_platform_interface flutter] - uni_links_web2 0.1.0+2 [uni_links2_platform_interface flutter flutter_web_plugins] - universal_io 2.0.4 [collection crypto meta typed_data] - url_launcher_linux 2.0.0 [flutter] - url_launcher_macos 2.0.0 [flutter] - url_launcher_platform_interface 2.0.2 [flutter plugin_platform_interface] - url_launcher_web 2.0.0 [url_launcher_platform_interface meta flutter flutter_web_plugins] - url_launcher_windows 2.0.0 [flutter] - value_layout_builder 0.2.0 [flutter] - vector_math 2.1.0 - video_player_platform_interface 4.1.0 [flutter meta flutter_test] - video_player_web 2.0.0 [flutter flutter_web_plugins meta video_player_platform_interface] - vm_service 5.5.0 [meta] - wakelock 0.4.0 [flutter meta wakelock_macos wakelock_platform_interface wakelock_web] - wakelock_macos 0.1.0 [flutter flutter_web_plugins wakelock_platform_interface] - wakelock_platform_interface 0.2.0 [flutter meta] - wakelock_web 0.2.0 [flutter flutter_web_plugins js wakelock_platform_interface] - watcher 1.0.0 [async path pedantic] - web_socket_channel 2.0.0 [async crypto stream_channel] - webdriver 2.1.2 [archive matcher path stack_trace sync_http] - webkit_inspection_protocol 1.0.0 [logging] - win32 2.0.5 [ffi] - xdg_directories 0.2.0 [meta path process] - xml 4.5.1 [collection convert meta petitparser] - yaml 2.2.1 [charcode collection string_scanner source_span] ```

markusaksli-nc commented 3 years ago

Hi @MrAlek Is this without signing out the anonymous user before signing in with the credential? Thank you

MrAlek commented 3 years ago

@markusaksli-nc No exactly, the exception is caught and signInWithCredential is run without signing out first.

If we just do signInWithCredential right away without trying currentUser.linkWithCredential(phoneCredential) it works in both cases (existing phone number & non-existing phone number), but the anonymous user then always gets discarded.

markusaksli-nc commented 3 years ago

Could not reproduce this with a test number, does it only reproduce with a real device?

Could you provide a code sample?

MrAlek commented 3 years ago

It only reproduces with a real phone number since the error thrown back is code: session-expired, message: The SMS code has expired. Please re-send the verification code to try again..

You don't necessarily need a real device, just a real phone number.

MrAlek commented 3 years ago

I could perhaps provide a sample project using the example in this repo. Essentially step 3+4 is the only thing out of the ordinary. For that, I provided sample code in the description.

markusaksli-nc commented 3 years ago

Was able to reproduce this with firebase_auth: ^1.1.3

flutter doctor -v ``` [✓] Flutter (Channel master, 2.3.0-1.0.pre.227, on macOS 11.2.3 20D91 darwin-arm, locale en-GB) • Flutter version 2.3.0-1.0.pre.227 at /Users/nevercode/development/flutter_master • Upstream repository https://github.com/flutter/flutter.git • Framework revision 7f1d1414cc (23 hours ago), 2021-05-04 06:17:19 -0700 • Engine revision 378e4dbc41 • Dart version 2.14.0 (build 2.14.0-48.0.dev) [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3) • Android SDK at /Users/nevercode/Library/Android/sdk • Platform android-30, build-tools 30.0.3 • Java binary at: /Users/nevercode/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.7199119/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS • Xcode at /Applications/Xcode.app/Contents/Developer • Xcode 12.5, Build version 12E262 • CocoaPods version 1.10.1 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 4.1) • Android Studio at /Users/nevercode/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.7199119/Android Studio.app/Contents • Flutter plugin can be installed from: � https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: � https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495) [✓] Android Studio (version 4.1) • Android Studio at /Users/nevercode/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.7199119/Android Studio.app/Contents • Flutter plugin can be installed from: � https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: � https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495) [✓] Connected device (3 available) • Nevercode’s iPhone (mobile) • b668e524315069f3db3661ac11ff1f66afafebdb • ios • iOS 14.5 • macOS (desktop) • macos • darwin-arm64 • macOS 11.2.3 20D91 darwin-arm • Chrome (web) • chrome • web-javascript • Google Chrome 90.0.4430.93 • No issues found! ```
MrAlek commented 3 years ago

I was able to reproduce it on Android now too so my original assumption about this being an iOS issue was wrong.

mminhlequang commented 3 years ago

@MrAlek Looks like signInWithCredential function is called twice Please check this!

russellwheatley commented 3 years ago

Hey @MrAlek, this appears to be a documentation issue I'm afraid. It is possible to use the same credential for other providers (e.g. google), but the phone provider appears to require a fresh sms code once an attempt at linking the credential has been made.

MrAlek commented 3 years ago

@russellwheatley thanks for getting back. Too bad this was a "It's not a bug, it's a feature" type of thing.

I guess next thing would be to report this as a bug in the Firebase iOS / Android SDKs as this prohibits the intended behavior of linking credentials & "upgrading" an anonymous user when using phone auth.

russellwheatley commented 3 years ago

Yeah, sorry, I was going to mention that you ought to raise this as an issue with Firebase upstream. This library doesn't choose how the Firebase auth works internally, and it does appear in my humble opinion, like it ought to work as you've set up. But perhaps there's some reason that I'm not aware of.