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.57k stars 3.94k forks source link

[firebase_messaging]: getToken() does not work on Safari, works on Chrome and Firefox. 'Request contains an invalid argument' #13048

Open andynewman10 opened 1 month ago

andynewman10 commented 1 month ago

Is there an existing issue for this?

Which plugins are affected?

Messaging

Which platforms are affected?

Web

Description

Firebase Cloud Messaging does not work on Safari, getToken() raises an exception. Works fine with Chrome and Firefox.

Reproducing the issue

You need a valid Firebase account and a vapidKey to replicate this issue with the instructions below.

Please carefully follow the steps described here to replicate the issue.

----Code preparation----

  1. Create a new Flutter application with flutter create firetestapp --platforms web. In the web folder, add an empty firebase-messaging-sw.js file (I usually add a comment in this file to be on the safe side).
  2. In the pubspec.yaml file, add the following dependencies
    firebase_core: ^3.1.1
    firebase_messaging: ^15.0.2

    The same issue can be seen with firebase_core: ^2.32.0 and firebase_messaging: ^14.9.4.

  3. From your prompt do a dart pub global activate flutterfire_cli. For my tests, I used firebase-tools@13.8.3
  4. firebase login. Log yourself in if necessary.
  5. flutterfire configure. Select your Firebase project, just select the 'web' target and let the program do its job.
  6. Modify the main.dart file this way:

Add this at the start of the file:

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';

and change the _incrementCounter() function to look like this (do a copy/paste):

  void _incrementCounter() async {
    try {
      setState(() {
        _text = "Calling initializeApp";
      });

      await Firebase.initializeApp(
          options: DefaultFirebaseOptions.currentPlatform);

      setState(() {
        _text = "Calling getToken";
      });

      // Wait a bit, this works around a bug whereby the service worker may not be ready yet, see:
      // https://github.com/firebase/firebase-js-sdk/issues/7575
      // https://github.com/firebase/flutterfire/issues/12415
      // https://github.com/firebase/flutterfire/issues/11954
      await Future.delayed(const Duration(seconds: 2), () {});

      const vapidKey =
          'PUT_YOUR_VAPID_KEY_HERE';
      // The problem happens here
      var token = await FirebaseMessaging.instance.getToken(vapidKey: vapidKey);

      setState(() {
        _text = token ?? "null";
        _counter++;
      });
    } catch (err) {
      setState(() {
        _text = err.toString();
      });
    }
  }

----Running the code----

  1. Run the app on Safari. I use macOS Ventura, Safari 17.5.
  2. Click the '+' button at the bottom right corner of the page
  3. When the notification permission popup is displayed, click the 'Authorize' button.
  4. Surprisingly, even though the 'Authorize' button was clicked, an exception is raised: [firebase_messaging/permission_blocked] Messaging: The notification permission was not granted and blocked instead.
  5. Refresh the page. Click the '+' button again. This time, the notification permission dialog does not show up, suggesting the permission is granted (as it should be). It can be verified that the permission at this point is indeed granted by calling FirebaseMessaging.instance.requestPermission(), which does return AuthorizationStatus.granted.
  6. At this point the authorization is granted. getToken() now raises the following exception: [firebase_messaging/token-subscribe-failed] Messaging: A problem occurred while subscribing the user to FCM. Request contains an invalid argument.

In the Safari Developer Tools, one can see that service workers are running (both flutter's and firebase's (firebase-messaging-sw.js))

Firebase Core version

3.1.1

Flutter Version

3.22.2

Relevant Log Output

See the exception text in the code above.

Flutter dependencies

Expand Flutter dependencies snippet
```yaml Dart SDK 3.4.3 Flutter SDK 3.22.2 firetestapp 1.0.0+1 dependencies: - cupertino_icons 1.0.8 - firebase_auth 5.1.1 [firebase_auth_platform_interface firebase_auth_web firebase_core firebase_core_platform_interface flutter meta] - firebase_core 3.1.1 [firebase_core_platform_interface firebase_core_web flutter meta] - firebase_messaging 15.0.2 [firebase_core firebase_core_platform_interface firebase_messaging_platform_interface firebase_messaging_web flutter meta] - flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine] dev dependencies: - flutter_lints 3.0.2 [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.38 [collection firebase_core firebase_core_platform_interface flutter meta] - 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 - fake_async 1.3.1 [clock collection] - firebase_auth_platform_interface 7.4.1 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - firebase_auth_web 5.12.3 [firebase_auth_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins http_parser meta web] - firebase_core_platform_interface 5.1.0 [collection flutter flutter_test meta plugin_platform_interface] - firebase_core_web 2.17.2 [firebase_core_platform_interface flutter flutter_web_plugins meta web] - firebase_messaging_platform_interface 4.5.40 [_flutterfire_internals firebase_core flutter meta plugin_platform_interface] - firebase_messaging_web 3.8.10 [_flutterfire_internals firebase_core firebase_core_web firebase_messaging_platform_interface flutter flutter_web_plugins meta web] - flutter_web_plugins 0.0.0 [flutter characters collection material_color_utilities meta vector_math] - http_parser 4.0.2 [collection source_span string_scanner typed_data] - leak_tracker 10.0.4 [clock collection meta path vm_service] - leak_tracker_flutter_testing 3.0.3 [flutter leak_tracker leak_tracker_testing matcher meta] - leak_tracker_testing 3.0.1 [leak_tracker matcher meta] - 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.12.0 - path 1.9.0 - plugin_platform_interface 2.1.8 [meta] - 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.7.0 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph] - typed_data 1.3.2 [collection] - vector_math 2.1.4 - vm_service 14.2.1 - web 0.5.1 ```

Additional context and comments

The piece of code given in this issue works fine with both Google Chrome and Mozilla Firefox. Only Safari has a problem.

TarekkMA commented 1 month ago

@andynewman10 Thank you for reporting this issue. I've tested the example app found here: https://github.com/firebase/flutterfire/tree/master/packages/firebase_messaging/firebase_messaging/example, and I found that after clicking on request permissions and refreshing the page, it works without issues. Can you try and see if the example app works for you, or if you still face the same issue?

andynewman10 commented 1 month ago

Your example does not work on my setup when using Safari.

All tests done on macOS Ventura 13.6.7, Safari 17.5

  1. On Chrome, when the page appears, the Authorization popup is displayed, I click 'Authorize', then the FCM token eventually appears
  2. On Safari, I have created a desktop shortcut to force the activation of the Notification API (to be on the safe side). When I click on the shortcut to open the page
    • the page is displayed
    • no Authorization popup appears (which makes sense, since Notifications are already, de-facto enabled)
    • in the Javascript console, plenty of red errors are displayed (see log below)
    • clicking the 'Request permissions' immediately yields 'Authorization status: Authorized' in the top zone since Notification permission is de facto granted
    • FCM token : the Circular progress indications spins forever.
Javascript console output (click to expand) ``` Error from backend: Error: Bad state: Not connected to an application. Stack Trace: #0 DevHandler._handleConnection. (package:dwds/src/handlers/dev_handler.dart:272:13) #1 _rootRunUnary (dart:async/zone.dart:1407:47) #2 _CustomZone.runUnary (dart:async/zone.dart:1308:19) #3 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1217:7) #4 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:365:11) #5 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:297:7) #6 _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:123:11) #7 _MapStream._handleData (dart:async/stream_pipe.dart:218:10) #8 _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13) #9 _rootRunUnary (dart:async/zone.dart:1415:13) #10 _CustomZone.runUnary (dart:async/zone.dart:1308:19) #11 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1217:7) #12 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:365:11) #13 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:297:7) #14 _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:784:19) #15 _StreamController._add (dart:async/stream_controller.dart:658:7) #16 _StreamController.add (dart:async/stream_controller.dart:606:5) #17 new WebSocketImpl.fromSocket. (package:web_socket_channel/src/copy/web_socket_impl.dart:730:21) #18 _rootRunUnary (dart:async/zone.dart:1407:47) #19 _CustomZone.runUnary (dart:async/zone.dart:1308:19) #20 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1217:7) #21 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:365:11) #22 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:297:7) #23 _SinkTransformerStreamSubscription._add (dart:async/stream_transformers.dart:63:11) #24 _EventSinkWrapper.add (dart:async/stream_transformers.dart:13:11) #25 _WebSocketProtocolTransformer._messageFrameEnd (package:web_socket_channel/src/copy/web_socket_impl.dart:319:23) #26 _WebSocketProtocolTransformer.add (package:web_socket_channel/src/copy/web_socket_impl.dart:218:46) #27 _SinkTransformerStreamSubscription._handleData (dart:async/stream_transformers.dart:111:24) #28 _rootRunUnary (dart:async/zone.dart:1407:47) #29 _CustomZone.runUnary (dart:async/zone.dart:1308:19) #30 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1217:7) #31 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:365:11) #32 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:297:7) #33 _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:784:19) #34 _StreamController._add (dart:async/stream_controller.dart:658:7) #35 _StreamController.add (dart:async/stream_controller.dart:606:5) #36 _Socket._onData (dart:io-patch/socket_patch.dart:2449:41) #37 _rootRunUnary (dart:async/zone.dart:1415:13) #38 _CustomZone.runUnary (dart:async/zone.dart:1308:19) #39 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1217:7) #40 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:365:11) #41 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:297:7) #42 _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:784:19) #43 _StreamController._add (dart:async/stream_controller.dart:658:7) #44 _StreamController.add (dart:async/stream_controller.dart:606:5) #45 new _RawSocket. (dart:io-patch/socket_patch.dart:1936:33) #46 _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1379:14) #47 _microtaskLoop (dart:async/schedule_microtask.dart:40:21) #48 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5) #49 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:118:13) #50 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:185:5) ```
andynewman10 commented 1 month ago

@andynewman10 Thank you for reporting this issue. I've tested the example app found here: https://github.com/firebase/flutterfire/tree/master/packages/firebase_messaging/firebase_messaging/example, and I found that after clicking on request permissions and refreshing the page, it works without issues. Can you try and see if the example app works for you, or if you still face the same issue?

@TarekkMA how do you run the web page on Safari? Flutter for VS code cannot run or start a debug session on Safari, at least not directly. To test the page on Safari, I start a debug session on Chrome (with F5), which starts the development web server on, eg. http://localhost:45502. I then leave the page open in Chrome, and open the page separately with Safari.

Note that no matter how I test the page with Safari (requesting permissions then refreshing the page, like you ; or using a desktop shortcut to validate permissions beforehand, which I prefer to do), I get the same error.

andynewman10 commented 1 month ago

The problem happens even if I change my locale to US english, system-wide.

I tried with Firefox and Chrome and, again, there is no issue with these browsers.

andynewman10 commented 1 month ago

You can reproduce the issue with the example app by running the following command

flutter run --web-port 45502 -d web-server

Now test the page on Safari. Either just surf on the page then refresh it, or create a link on your desktop by dragging the URL from the browser address bar to the desktop.

The Javascript console with display the following errors

- Failed to load resource: the server responded with a status of 400 () - https://fcmregistrations.googleapis.com/v1/projects/flutterfire-e2e-tests/registrations
- Unhandled Promise Rejection: Error - throw core_patch.dart 265

Using Flutter 3.22.2, firebase_core 3.1.1, firebase_messaging 5.1.1.

In the Javascript console, if I click on https://fcmregistrations.googleapis.com/v1/projects/flutterfire-e2e-tests/registrations I get:

Request:

{"web":{"endpoint":"","auth":"","p256dh":"","applicationPubKey":"BNKkaUWxyP_yC_lki1kYazgca0TNhuzt2drsOrL6WrgGbqnMnr8ZMLzg_rSPDm6HKphABS0KzjPfSqCXHXEd06Y"}}

Response:

{
  "error:" {
    "code": 400,
    "message": "Request contains an invalid argument.",
    "status", "INVALID_ARGUMENT"
  }
}
andynewman10 commented 1 month ago

Full request headers:

:method: POST
:scheme: https
:authority: fcmregistrations.googleapis.com
:path: /v1/projects/flutterfire-e2e-tests/registrations
Accept: application/json
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9
Connection: keep-alive
Content-Length: 155
Content-Type: application/json
Host: fcmregistrations.googleapis.com
Origin: http://localhost:49555
Referer: http://localhost:49555/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15
x-goog-api-key: AIzaSyB7wZb2tO1-Fs6GbDADUSTs2Qs3w08Hovw
x-goog-firebase-installations-auth: FIS eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBJZCI6IjE6NDA2MDk5Njk2NDk3OndlYjo4N2UyNWU1MWFmZTk4MmNkMzU3NGQwIiwiZXhwIjoxNzIxMTEzMjk2LCJmaWQiOiJkOEpKd0duZ1Yxd08wXzBTVmFCdl85IiwicHJvamVjdE51bWJlciI6NDA2MDk5Njk2NDk3fQ.AB2LPV8wRQIgHlR_4Fc5-U4Tno_hu29HTPX07VKSia4CtJEVwJ5rUs8CIQDYFRLfE_keKW8MF2SpESOu96vXy5ae3gH1aBODFu6ZhQ

I tried disabling CORS restrictions in Safari, didn't make any difference

andynewman10 commented 1 month ago

I see a similar issue has been reported here:

https://github.com/firebase/firebase-js-sdk/issues/6620#issuecomment-2049382148

TarekkMA commented 1 month ago

Thank you for the update

andynewman10 commented 1 month ago

@TarekkMA Can I ask you what macOS version you are using? Are you by chance using macOS Sonoma?

TarekkMA commented 1 month ago

Yes, and this is my macos version: 14.5 (23F79)

andynewman10 commented 1 month ago

Thanks for your quick reply. @andreififiita, like me, is using macOS Ventura 13.6.x. I wonder if this detail plays a role in the problem we observe. We are both using Safari 17.4/17.5.

andreififiita commented 1 month ago

@TarekkMA @andynewman10 That is correct, i am using macOS Ventura 13.6.7. So we can confirm that this issue appears in Safari on macOS Sonoma as well? I am curious if anyone had managed to make it work properly on Sonoma.

andynewman10 commented 1 month ago

@TarekkMA @andreififiita I suspect the problem might only affect Ventura. It would be a valuable piece of information for the firebase-js-sdk people to know if the problem is Ventura-related. Unfortunately I just have one Mac with Ventura, with no possibility to upgrade (MacBook A1708 from 2017).