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.6k stars 3.95k forks source link

πŸ› [firebase_auth_web] Session doesn't resume if firebase app check is configured #12452

Closed OutdatedGuy closed 3 months ago

OutdatedGuy commented 6 months ago

Bug report

Describe the bug

When Firebase App check for Firebase Auth is enabled/enforced on Firebase dashboard and added initialization code in flutter app following is happening:

When logged in on Flutter web using firebase auth's any sign in method, everything works fine as long as the current working tab is NOT closed and reopened or a new tab with same url is NOT opened simultaneously.

So if a user is logged in, then they stay logged in while using the web app, after hitting refresh button or even after duplicating the tab. But as soon as they open another tab with the same url or close the current tab and re-open it again, their session is lost.

This is only happening only on web when Firebase app check is configured and NOT on Android or iOS.

I looked into it and when re-opening/re-visiting the app, Firebase Auth doesn't send App Check token to the firebase server. Hence the issue is happening.

The error it prints while retrieving current user details when re-opening the tab is below:

{
  "error": {
    "code": 401,
    "message": "Firebase App Check token is invalid.",
    "errors": [
      {
        "message": "Firebase App Check token is invalid.",
        "domain": "global",
        "reason": "unauthorized"
      }
    ],
    "status": "UNAUTHENTICATED"
  }
}

Steps to reproduce

Steps to reproduce the behavior:

  1. Copy the sample code in lib/main.dart
  2. Add necessary dependencies
  3. Configure a test firebase project and enable Firebase Auth (Don't enable App Check yet)
  4. Create a dummy email password account
  5. Run flutterfire configure command on flutter project
  6. Build the project for web in release mode
  7. Host it somewhere
  8. Try logging in
  9. Try refreshing, duplicating tab, closing & reopening current tab and opening another tab simultaneously with same url
  10. See everything still works fine
  11. Now configure app check in Firebase for auth
  12. Register your web app with all the standard procedures
  13. Un-comment the app check code from sample code
  14. Add recaptcha site key in place of your_site_key_here
  15. And repeat steps 6-9
  16. See that session is lost when re-opening current tab or opening another tab simultaneously with same url

Expected behavior

The current session resumes and no-app check error is logged.

Sample code

// import 'package:firebase_app_check/firebase_app_check.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:flutter_application_1/firebase_options.dart';

void main() async {
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  // // Firebase App Check
  // await FirebaseAppCheck.instance.activate(
  //   webProvider: ReCaptchaEnterpriseProvider(
  //     'your_site_key_here',
  //   ),
  // );

  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final auth = FirebaseAuth.instance;

  @override
  void initState() {
    super.initState();
    auth.authStateChanges().listen((user) {
      setState(() {});
    });
    auth.currentUser?.reload();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: auth.currentUser == null
          ? SignInScreen(
              providers: [EmailAuthProvider()],
              actions: [
                AuthStateChangeAction<SignedIn>((context, state) {}),
              ],
            )
          : Scaffold(
              appBar: AppBar(
                title: const Text('Home Page'),
                actions: [
                  IconButton(
                    icon: const Icon(Icons.exit_to_app),
                    onPressed: FirebaseAuth.instance.signOut,
                  ),
                ],
              ),
              body: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(
                      'Welcome, ${FirebaseAuth.instance.currentUser!.uid}',
                    ),
                    const Text('You are now signed in!'),
                  ],
                ),
              ),
            ),
    );
  }
}

Additional context

Here is a demo I have already hosted for you to test with the sample code and Firebase App Check enforced and initialized: https://flutter-web-auth-error.netlify.app/

Demo Credentials **Email:** webauth@error.com **Password:** 123456

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, 3.19.2, on macOS 14.3.1 23D60 darwin-arm64, locale en-IN) [βœ“] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [βœ“] Xcode - develop for iOS and macOS (Xcode 15.3) [βœ“] Chrome - develop for the web [βœ“] Android Studio (version 2023.1) [βœ“] VS Code (version 1.85.1) [βœ“] VS Code (version 1.88.0-insider) [βœ“] Connected device (2 available) [βœ“] Network resources β€’ No issues found! ```

Flutter dependencies

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

Click To Expand ``` Dart SDK 3.3.0 Flutter SDK 3.19.2 flutter_application_1 1.0.0+1 dependencies: - cupertino_icons 1.0.6 - firebase_app_check 0.2.1+16 [firebase_app_check_platform_interface firebase_app_check_web firebase_core firebase_core_platform_interface flutter] - firebase_auth 4.17.7 [firebase_auth_platform_interface firebase_auth_web firebase_core firebase_core_platform_interface flutter meta] - firebase_core 2.26.0 [firebase_core_platform_interface firebase_core_web flutter meta] - firebase_ui_auth 1.13.1 [email_validator firebase_auth firebase_core firebase_dynamic_links firebase_ui_localizations firebase_ui_oauth firebase_ui_shared flutter flutter_localizations meta] - flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine] - flutter_login 5.0.0 [another_flushbar another_transformer_page_view flutter font_awesome_flutter intl_phone_number_input phone_numbers_parser provider quiver sign_in_button url_launcher] dev dependencies: - flutter_lints 3.0.1 [lints] - flutter_test 0.0.0 [flutter test_api matcher path fake_async clock stack_trace vector_math leak_tracker_flutter_testing async boolean_selector characters collection leak_tracker leak_tracker_testing material_color_utilities meta source_span stream_channel string_scanner term_glyph vm_service] transitive dependencies: - _flutterfire_internals 1.3.24 [collection firebase_core firebase_core_platform_interface flutter meta] - another_flushbar 1.12.30 [flutter] - another_transformer_page_view 2.0.1 [flutter] - args 2.4.2 - async 2.11.0 [collection meta] - boolean_selector 2.1.1 [source_span string_scanner] - characters 1.3.0 - clock 1.1.1 - collection 1.18.0 - crypto 3.0.3 [typed_data] - desktop_webview_auth 0.0.15 [crypto flutter http flutter_web_plugins plugin_platform_interface] - email_validator 2.1.17 - equatable 2.0.5 [collection meta] - fake_async 1.3.1 [clock collection] - firebase_app_check_platform_interface 0.1.0+18 [_flutterfire_internals firebase_core flutter meta plugin_platform_interface] - firebase_app_check_web 0.1.0+18 [_flutterfire_internals firebase_app_check_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins js] - firebase_auth_platform_interface 7.1.7 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - firebase_auth_web 5.9.7 [firebase_auth_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins http_parser js meta web] - firebase_core_platform_interface 5.0.0 [collection flutter flutter_test meta plugin_platform_interface] - firebase_core_web 2.11.5 [firebase_core_platform_interface flutter flutter_web_plugins js meta web] - firebase_dynamic_links 5.4.16 [firebase_core firebase_core_platform_interface firebase_dynamic_links_platform_interface flutter meta plugin_platform_interface] - firebase_dynamic_links_platform_interface 0.2.6+24 [_flutterfire_internals firebase_core flutter meta plugin_platform_interface] - firebase_ui_localizations 1.10.2 [flutter flutter_localizations path] - firebase_ui_oauth 1.5.1 [desktop_webview_auth firebase_auth firebase_ui_auth firebase_ui_shared flutter_svg flutter] - firebase_ui_shared 1.4.1 [flutter] - flutter_localizations 0.0.0 [flutter intl characters clock collection material_color_utilities meta path vector_math] - flutter_svg 2.0.10+1 [flutter http vector_graphics vector_graphics_codec vector_graphics_compiler] - flutter_web_plugins 0.0.0 [flutter characters collection material_color_utilities meta vector_math] - font_awesome_flutter 10.7.0 [flutter] - http 1.2.0 [async http_parser meta web] - http_parser 4.0.2 [collection source_span string_scanner typed_data] - intl 0.18.1 [clock meta path] - intl_phone_number_input 0.7.4 [flutter libphonenumber_plugin equatable collection] - js 0.6.7 [meta] - leak_tracker 10.0.0 [clock collection meta path vm_service] - leak_tracker_flutter_testing 2.0.1 [flutter leak_tracker leak_tracker_testing matcher meta] - leak_tracker_testing 2.0.1 [leak_tracker matcher meta] - libphonenumber_platform_interface 0.4.2 [flutter plugin_platform_interface] - libphonenumber_plugin 0.3.3 [flutter flutter_web_plugins libphonenumber_platform_interface libphonenumber_web] - libphonenumber_web 0.3.2 [flutter flutter_web_plugins js libphonenumber_platform_interface] - lints 3.0.0 - matcher 0.12.16+1 [async meta stack_trace term_glyph test_api] - material_color_utilities 0.8.0 [collection] - meta 1.11.0 - nested 1.0.0 [flutter] - path 1.9.0 - path_parsing 1.0.1 [vector_math meta] - petitparser 6.0.2 [meta] - phone_numbers_parser 8.2.1 [meta] - plugin_platform_interface 2.1.8 [meta] - provider 6.1.2 [collection flutter nested] - quiver 3.2.1 [matcher] - sign_in_button 3.2.0 [flutter font_awesome_flutter] - sky_engine 0.0.99 - source_span 1.10.0 [collection path term_glyph] - stack_trace 1.11.1 [path] - stream_channel 2.1.2 [async] - string_scanner 1.2.0 [source_span] - term_glyph 1.2.1 - test_api 0.6.1 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph] - typed_data 1.3.2 [collection] - url_launcher 6.2.5 [flutter url_launcher_android url_launcher_ios url_launcher_linux url_launcher_macos url_launcher_platform_interface url_launcher_web url_launcher_windows] - url_launcher_android 6.3.0 [flutter url_launcher_platform_interface] - url_launcher_ios 6.2.5 [flutter url_launcher_platform_interface] - url_launcher_linux 3.1.1 [flutter url_launcher_platform_interface] - url_launcher_macos 3.1.0 [flutter url_launcher_platform_interface] - url_launcher_platform_interface 2.3.2 [flutter plugin_platform_interface] - url_launcher_web 2.2.3 [flutter flutter_web_plugins url_launcher_platform_interface web] - url_launcher_windows 3.1.1 [flutter url_launcher_platform_interface] - vector_graphics 1.1.11+1 [flutter http vector_graphics_codec] - vector_graphics_codec 1.1.11+1 - vector_graphics_compiler 1.1.11+1 [args meta path_parsing xml vector_graphics_codec path] - vector_math 2.1.4 - vm_service 13.0.0 - web 0.4.2 - xml 6.5.0 [collection meta petitparser] ```

Lyokone commented 5 months ago

Hello @OutdatedGuy, after investigation, we can only reproduce the issue when opening another tab. Can you try enablePersistence(const PersistenceSettings(synchronizeTabs: true))?

OutdatedGuy commented 5 months ago

@Lyokone have you followed all the mentioned steps because if you try the provided demo in Additional Context section, you can see it even gives error even when closing and re-opening the tab.

https://github.com/firebase/flutterfire/assets/74326345/ef610c58-16a3-4fb5-8f01-73418c4a6ca9

OutdatedGuy commented 5 months ago

@Lyokone Anyway I'll try the code provided by you to see if it solves anything πŸ‘πŸΌ

Lyokone commented 5 months ago

@OutdatedGuy as said in my previous message, we don't reproduce your finding. You can check the example app here: https://flutterfire-e2e-tests.web.app/

OutdatedGuy commented 5 months ago

@OutdatedGuy as said in my previous message, we don't reproduce your finding. You can check the example app here: https://flutterfire-e2e-tests.web.app/

@Lyokone I tried it, but I'm unable to see any App Check token being sent in the network tab for the accounts lookup API. Have you initialized Firebase App Check as I mentioned in the above steps?

OutdatedGuy commented 5 months ago

Hello @OutdatedGuy, after investigation, we can only reproduce the issue when opening another tab. Can you try enablePersistence(const PersistenceSettings(synchronizeTabs: true))?

Where is this function? I'm unable to find it. I can only see below option:

FirebaseAuth.instance.setPersistence(Persistence.INDEXED_DB);
Lyokone commented 5 months ago

@OutdatedGuy Sorry, I thought the version deployed had Firebase App Check, it's the case now and it seems that I can reload the page without issues with issues: https://flutterfire-e2e-tests.web.app/

Can you try to use firebase hosting to see if it's not your hosting provider causing these issues?

Sorry about synchronizeTabs I thought this setting existed in Auth but it only exists in Firestore

OutdatedGuy commented 5 months ago

@Lyokone, I tried the example provided by you and it is working properly, but when I copy the Accounts Lookup API with key and idToken and WITHOUT X-Firebase-Appcheck and send a request using postman it still doesn't give the UNAUTHENTICATED error.

I see you have initialized App Check on Frontend but have you enforced it in the Firebase Console for Phone Auth? Please check once.

OutdatedGuy commented 5 months ago

@Lyokone tried hosting it on Firebase Hosting and it is still giving the same issue.

Link: https://flutter-web-auth-error.web.app/

Demo Credentials **Email:** webauth@error.com **Password:** 123456
Lyokone commented 5 months ago

I've enforced it in the console as well:

Screenshot 2024-03-13 at 07 51 09

EDIT: it seems that CMD-MAJ-T to restore a tab wasn't triggering the issue, but opening another new fresh tab is triggering. Thanks for helping me reproduce. I'll see if it's caused by the native SDK or the Flutter SDK.

OutdatedGuy commented 5 months ago

@Lyokone did you find the cause of this issue?

After watching the network tab, I think the issue is that the Auth SDK is not waiting till the App Check token is first fetched when the site is opened in a new-fresh tab.

Also the Accounts Lookup API is initiated below the recaptcha scripts are loaded.

Lyokone commented 5 months ago

Hello @OutdatedGuy , we’re discussing this internally with the relevant teams, and we will update this issue when we have any further information to share. Thank you for your patience!

OutdatedGuy commented 4 months ago

@Lyokone is this issue fixed? I'm unable to reproduce it after updating the firebase_auth package to latest version (v4.19.4).

You can close this issue now I suppose.

TarekkMA commented 3 months ago

@OutdatedGuy, thank you for confirming that this issue is now fixed. If you find that it is still persisting, please don't hesitate to open a new issue. Closing this issue.