firebase-android-sdk icon indicating copy to clipboard operation
firebase-android-sdk copied to clipboard

Allow FirebaseMessaging to be initialized with a secondary FirebaseApp

Open rnatour opened this issue 4 years ago • 7 comments

What feature would you like to see?

The Firebase Cloud Messaging documentation outlines an approach for allowing multiple senders to send push notifications to the same app, however the wording is cryptic and there don't appear to be any code samples for how to do this.

After digging around, it looks like there used to be (circa 2018) a way of accomplishing this by calling FirebaseInstanceId.getInstance() .getToken("senderId1,senderId2", FirebaseMessaging.INSTANCE_ID_SCOPE). This would allow for the sender id of the secondary Firebase project to be specified when the token is created, however FirebaseInstanceId is now deprecated.

In the source code for the FirebaseMessaging class, there is a package-private initializer that takes in a FirebaseApp object. This looks like it should be the correct way of generating FCM registration tokens for secondary Firebase apps, and indeed if I use reflection to access this initializer method and generate tokens using FirebaseMessaging.getInstance(**secondaryApp**).getToken().addOnCompleteListener(...) I am able to send push notifications successfully, however this is a subpar solution for obvious reasons.

It'd be great to support this method for public use, or at least have more clarity on what the current recommended approach for using FCM with multiple projects in the same app is.

How would you use it?

I am developing an SDK that uses FCM to receive data push notifications from my backend. The SDK should function correctly whether the host app is also using Firebase or not. Currently, when the host app is using Firebase FirebaseMessaging picks up on the initialization parameters of the host app instead of my SDK. When I attempt to send push notifications from my server to these FCM registration tokens, I get SenderId mismatch errors (as I would expect if the tokens belong to a different project). If I use reflection to instantiate FirebaseMessaging with my SDK's FirebaseApp or if the host app is not using Firebase, I am able to send push notifications successfully.

rnatour avatar Dec 27 '20 09:12 rnatour

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

google-oss-bot avatar Dec 27 '20 09:12 google-oss-bot

I have verified a way to do it as I had been facing a similar issue.

I registered one of the projects using the google-services.json file.

Now as per the documentation :

public void onNewToken (String token)

Called when a new token for the default Firebase project is generated.

Here the word "default" is of key importance. It mentions that the onNewToken method in the overridden FirebaseMessagingService (eg: MyFirebaseMessagingService) will only be called for the default project.

Hence in this case the first project configured using the google-services.json will be the default project and for that the onNewToken method will be called.

For the second project, I manually configured the project using the code below following the documentation:

val options = FirebaseOptions.Builder()
        .setProjectId("my-firebase-project")
        .setApplicationId("1:27992087142:android:ce3b6448250083d1")
        .setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
        .build()

The values for the parameters can be obtained from the google-services.json file of the second project. (NOTE: DON'T INCLUDE THE SECOND PROJECT'S google-services.json IN THE PROJECT)

google-services.json to manual code mapping

  1. projectId (setProjectId) : project_id key in the root of the json
  2. applicationid (setApplicationId): client > client_info > mobilesdk_app_id. Incase of multiple project make sure that the client used is of the package_name which matches the Android app
  3. apiKey (setApiKey) : client > api_key > current_key (make sure of the package name here as well.

KEY CODE

The most important part here which is tough to find in the documentation is to get the token of the second firebase project.

val app = Firebase.initialize(this, options, "ANY_FIXED_STRING_EXCEPT_DEFAULT")

val firebaseMessaging = app.get(FirebaseMessaging::class.java) as FirebaseMessaging

ymFirebaseMessaging.token.addOnCompleteListener{
                if (!it.isSuccessful) {
                    Log.d(TAG, "Fetching FCM token failed", it.exception)

                    return@addOnCompleteListener
                }


                val token = it.result
                Log.d(TAG, "YM: $token")
                Toast.makeText(
                    activity,
                    "$TAG: Got token",
                    Toast.LENGTH_LONG
                ).show()
}

tsherdiwala avatar Jan 23 '21 20:01 tsherdiwala

@rnatour refer the answer above or the stack overflow link: https://stackoverflow.com/a/65863769/1161911

tsherdiwala avatar Jan 23 '21 20:01 tsherdiwala

Good find on using get to retrieve a FirebaseMessaging instance for the secondary app. Much better than using reflection as I was doing.

I don't see this method included in any documentation, but for those that are curious the code link is here.

I'd still be interested in learning if there's any way this solution could interfere with the default app's FirebaseMessaging.

rnatour avatar Jan 24 '21 03:01 rnatour

@rlazo this seems to be stuck?

Manuito83 avatar Aug 11 '22 16:08 Manuito83

@cpsloal

rlazo avatar Aug 11 '22 16:08 rlazo

tsherdiwala

Your code works as expected, however the FirebaseApp#get method is annotated as @KeepForSdk.

This is the reason why this api not appear in official javadoc (https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseApp)

Here a clarification from the Firebase SDK team i think is required.

If the latest versions of firebase SDK are not able to support multiple sender id this is a point tha should be marked to make more aware the developers in case of product / project migrations

@cpsloal : What do you think about it?

EnricoMazzu avatar Sep 11 '22 17:09 EnricoMazzu

Now as per the documentation :

public void onNewToken (String token) Called when a new token for the default Firebase project is generated.

Here the word "default" is of key importance. It mentions that the onNewToken method in the overridden FirebaseMessagingService (eg: MyFirebaseMessagingService) will only be called for the default project.

So what happens when the token for the secondary app is invalidated while the app is running? Can we expect to be notified at all? Or is this a bug and we only receive a notification when the default token is invalidated. Here it mentions that onNewToken should be called if the "security of the token has been compromised". Is there any way to test this?

Chozzle avatar May 03 '23 05:05 Chozzle