4

I want to send a message to FCM topics from within my Android app. Sending the message through the Firebase console is working well, but once a user executes a particular action, I want a message to be sent to all other users who have subscribed to a particular topic.

In the documentation there is this code:

// The topic name can be optionally prefixed with "/topics/".
String topic = "highScores";

// See documentation on defining a message payload.
Message message = Message.builder()
.putData("score", "850")
.putData("time", "2:45")
.setTopic(topic)
.build();

// Send a message to the devices subscribed to the provided topic.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

I can't figure out from which class Message is. It is obviously not RemoteMessage.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
DeKekem
  • 589
  • 10
  • 20
  • 1
    There is no way to send messages directly from one Android device to another device with Firebase Cloud Message. You will always need a server (or otherwise trusted environment) to do that. See https://firebase.google.com/docs/cloud-messaging#how_does_it_work and my answer here: https://stackoverflow.com/questions/37990140/how-to-send-one-to-one-message-using-firebase-messaging. The example you've found seems at first glance for sending messages from your app to your own App Server through FCM, although I can't immediately find it in the docs. – Frank van Puffelen May 02 '19 at 13:52
  • Here is the doc https://firebase.google.com/docs/cloud-messaging/android/topic-messaging – DeKekem May 02 '19 at 14:09
  • That sample is using the Admin SDK, which is meant to be run in a trusted environment and can't be used in your Android app. – Frank van Puffelen May 02 '19 at 16:01

4 Answers4

5

You can do it by Volley and FCM API

here an example to send notification from user to "newOrder" Topic and have title and body

RequestQueue mRequestQue = Volley.newRequestQueue(this);

        JSONObject json = new JSONObject();
        try {
            json.put("to", "/topics/" + "newOrder");
            JSONObject notificationObj = new JSONObject();
            notificationObj.put("title", "new Order");
            notificationObj.put("body", "New order from : " + phoneNum.replace("+", " "));
            //replace notification with data when went send data
            json.put("notification", notificationObj);

            String URL = "https://fcm.googleapis.com/fcm/send";
            JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, URL,
                    json,
                    response -> Log.d("MUR", "onResponse: "),
                    error -> Log.d("MUR", "onError: " + error.networkResponse)
            ) {
                @Override
                public Map<String, String> getHeaders() {
                    Map<String, String> header = new HashMap<>();
                    header.put("content-type", "application/json");
                    header.put("authorization", "key=yourKey");
                    return header;
                }
            };


            mRequestQue.add(request);
        } catch (JSONException e) {
            e.printStackTrace();
        }

Replace yourKey with server key in your project in firebase


UPDATE : as @frank say in correct answer You will always need a server (or otherwise trusted environment) to hold yourKey and make it not public

so this answer is already work and can send notifications from android to topic or token

but if anyone take your key can send also to your apps notifications in any time so i suggest to use firebase functions or any service on your server just make sure your key in trusted environment and not reachable

also when get key there two type : 1 - Server key 2 - Legacy server key

like firebase say below also i suggested to use first because is more flexible to change or deleted

Firebase has upgraded our server keys to a new version. You may continue to use your Legacy server key, but it is recommended that you upgrade to the newest version

Mahmoud Abu Alheja
  • 3,343
  • 2
  • 24
  • 41
3

There is no way to securely send messages directly from one Android device to another device with Firebase Cloud Messaging. You will always need a server (or otherwise trusted environment) to do that. See this docs section showing how messages are sent and my answer. here: How to send one to one message using Firebase Messaging.

The code sample you shared is using the Admin SDK for Java to send a message, which is meant to be run in a trusted environment. It can't be used in your Android app.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • 1
    I eventually used cloud functions to realize what I wanted. The function listen for any write on a firestore collection and as a result send a message to the specific topic. – DeKekem May 03 '19 at 20:04
  • Google provides an api.. We can send a fcm message to another device from android device via sending api request to that api. You are wrong. – Samir Alakbarov Aug 18 '20 at 18:33
  • @SamirElekberov In your answer you are including the FCM **server** key in your client-side code. Doing this is a huge security risk, as anyone can take that key from your code, and use it to send whatever message they want to all your users. That's why the FCM server key should (as its name implies) only be used in trusted environments, such as your development machine, a server you control or Cloud Functions. – Frank van Puffelen Aug 18 '20 at 20:17
  • Yes but anyway it is not impossible.. And it is intended to use that way.. And how we can use api urls in our app if someone can get it ? They can send request to our api urls then right ? – Samir Alakbarov Aug 19 '20 at 20:54
  • I clarified my answer to "There is no way to securely send messages directly from one Android device to another device with Firebase Cloud Messaging". Doing so in not an intended use-case (I checked that repeatedly with the product team before I posted my first answer about this). I'm not sure I understand your questions though, so it might be worth posting a separate question about them if you don't find them addressed here or elsewhere. – Frank van Puffelen Aug 19 '20 at 22:39
0

Refer this link.. You can do what you need via this official google documentation.

But I can show you an example that I wrote via AndroidFastNetworking library:

        JSONObject dataJsonObject = new JSONObject();
        dataJsonObject.put("anyDataYouWant":"value");

        try {

            bodyJsonObject.put("to", "/topics/topic");
            bodyJsonObject.put("data", dataJsonObject);
            
        } catch (JSONException e) {
            e.printStackTrace();
        }
        
        
        
        
        AndroidNetworking.post("https://fcm.googleapis.com/fcm/send")
                .setContentType("application/json; charset=utf-8")
                .addJSONObjectBody(bodyJsonObject)
                .addHeaders("Authorization", "Your Firebase Authorization Key with 'key=' prefix:  ("key=AAAAjHK....") ")
                .setPriority(Priority.HIGH)
                .build()
                .getAsJSONObject(new JSONObjectRequestListener() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show();
                    }
                    
                    
                    @Override
                    public void onError(ANError anError) {
                        Toast.makeText(context, "Error: " + anError.toString(), Toast.LENGTH_SHORT).show();
                    }
                });
Samir Alakbarov
  • 1,120
  • 11
  • 21
  • 2
    You are including the FCM **server** key in your client-side code here, which is a huge security risk. Anyone can take that key from your code, and use it to send whatever message they want to all your users. That's why the FCM server key should (as its name implies) only be used in trusted environments, such as your development machine, a server you control or Cloud Functions. – Frank van Puffelen Aug 18 '20 at 20:18
-1

Here's an implementation using OkHttp 4.x and Kotlin:

// create the payload
val payload = JSONObject()
    .put("key", "value")       

// create the request body (POST request)
val mediaType = "application/json; charset=utf-8".toMediaType()
val requestBody = JSONObject()
    .put("to", "/topics/my_topic")
    .put("data", payload)
    .toString().toRequestBody(mediaType)

// create request
val request = Request.Builder()
    .url("https://fcm.googleapis.com/fcm/send")
    .post(requestBody)
    .addHeader("Authorization", "key=${server_key_please_replace}")
    .addHeader("Content-Type", "application/json")
    .build()

// execute the call
val response = OkHttpClient().newCall(request).execute()
val responseBody = response.body?.charStream()?.readLines()
val httpCode = response.code

// error handling goes here, it's an error if the http response code is not 200
// or if the responseBody contains an error message like
// [{"multicast_id":2633602252647458018,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}]

Security Considerations

It's not recommended to use the server API key on client side since the key can be extracted by an attacker and then used to send messages on behalf of the key owner (that would be you). I would however argue that the risk is quite low under certain circumstances:

  • Notification messages: the ability to send notifications to all app users is only a risk if those messages can do harm. In my case it's not possible to surface notifications because the app only processes data messages.
  • Data messages: in my case the app accepts specific topics but only when they are sent from the same user id (I use FCM to sync data for the same user across multiple devices). Without knowing all user ids an attacker could not even send messages. The user ids are impossible to retrieve because they are stored on the device and the user's Google Drive only so unless the Google Drive service as a whole is compromised this is a theoretical risk. On top of that, the data messages are harmless in my case.
  • The only real risk I see is that an attacker can launch a DOS attack so that Google shuts down access to FCM for your app. If FCM is business critical for your app then that's a real threat. BUT preventing the same attack against your backend API isn't trivial. If you have an API to send messages, that API needs to be protected. Having an endpoint to send messages might protect the FCM key but won't protect against a DOS attack per se. If users are not authenticated (in many apps they aren't) then providing that protection is a non trivial task (bot manager solutions are expensive and implementing mTLS between client and server isn't simple).

To summarize: while it's not recommended to use the server API key on client side, depending on the use case, the security risk is very low. Many apps can't afford to run a backend service just for that one functionality so I would argue using the server key client side can be justified in some cases.

Emanuel Moecklin
  • 28,488
  • 11
  • 69
  • 85
  • Please don't include the server key in any code that runs on a client. *Anyone* can download your app, extract the server key and start using it to send potentially malicious messages on your behalf. FCM server keys should only be used in trusted environments (e.g. your server). – Peter Friese May 25 '21 at 09:15
  • @PeterFriese while I agree in general, I disagree that it's a security risk in every case. Please see my security considerations – Emanuel Moecklin May 26 '21 at 09:27