Android Question FCM - Migrate from legacy HTTP to HTTP v1 - how to get access token for client to client messsaging?

GraemeW

Member
Licensed User
Longtime User
I've just uploaded my app to Play Store with a new feature which involves sending FCM notifications from one device to other devices which also have the app. Basically one device reads information from a web server and then tells the other devices so they don't have to bother the server.

I've adapted the legacy HTTP version described here FirebaseNotifications - Push messages / Firebase Cloud Messaging (FCM) - but placing the sending code inside my app. It works as intended.

However, PlayStore gives an error "Your app contains exposed Firebase Cloud Messaging (FCM) server keys." and gives a deadline of 21 January next year to update to the HTTP V1 API for FCM.

OK, so I've started out using Oauth2 to get the required access tokens, with scope = "https://www.googleapis.com/auth/firebase.messaging" - but now when my app attempts to get a token it opens a browser window to have the user enter an email address to log in to Google... which is not good. and now I'm completely confused.

Has anyone else gone down this road?
 
Solution
Well, that wasn't too bad. This very basic php server script forwards a message from the first device which detects a new earthquake to all the other devices. It achieves my original aim and moves the api key out of my app to a server, which should satisfy the Play Store gnomes. It's essentially a php version of Erel's original B4J-sendingtool in FirebaseNotifications - Push messages / Firebase Cloud Messaging (FCM). Hopefully problem solved.. ?
PHP:
<?php

//receives a topic+title+body message from an Android app and forwards it to Firebase Cloud Messaging
//FCM sends the message to other Android devices which are subscribed to the topic

//body is a JSON string - the sending service of the Android app on the originating device...

GraemeW

Member
Licensed User
Longtime User
Does it solve your problem?
No ... The Google OAuth 2 class you used is based on app user accounts - while the Migrate from legacy HTTP to HTTP v1 page indicates that a service account should be used. I'm working on this approach at the moment.. So far I've managed to assign GOOGLE_APPLICATION_CREDENTIALS to the service account json file path but haven't yet attempted to retrieve a token..


Untitled.png
 
Upvote 0

GraemeW

Member
Licensed User
Longtime User
and now I reach a dead-end because the B4X GoogleOAuth2 class doesn't seem to handle service account credentials.. and I don't have the skill to adapt it... :(
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
No ... The Google OAuth 2 class you used is based on app user accounts - while the Migrate from legacy HTTP to HTTP v1 page indicates that a service account should be used. I'm working on this approach at the moment.. So far I've managed to assign GOOGLE_APPLICATION_CREDENTIALS to the service account json file path but haven't yet attempted to retrieve a token..


View attachment 135208
Not sure what you mean. I have tested the notification is sent with my project.
 
Upvote 0

GraemeW

Member
Licensed User
Longtime User
Not sure what you mean. I have tested the notification is sent with my project.
I'm writing a B4A app rather than B4J so I'm trying to re-write the parts of your project that seem to fit my requirements. I have used Client IDs with type desktop and android but both end up with my app opening a browser window for whenever it attempts to get a token.

Either I'm missing something or the browser window is by design and and your approach will not solve my problem - as I need my app to send FCM messages without any user involvement (which is currently possible with Legacy HTTP). So far the browser windows (below) shows show error messages so I'm probably still missing something...

Screenshot_20221026-140932_Chrome.jpg
Screenshot_20221026-143536_Chrome.jpg
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
As mentioned by DonManfred, you should send the notification from a server.
What I can think of is to create a B4J server (or PHP web service) to handle the request.
Example is a REST API server which accept GET/POST or just a simple PHP page that trigger the CURL to send the push notification.
If you want to use an Android as a Http server, I worry it is not going to active 24/7 to standby and accept any request anytime.
 
Upvote 0

GraemeW

Member
Licensed User
Longtime User
- Send the Notifications from your Server and not from your B4A App. Send the token from the b4a apps to your server.
- No need to include the Serverkey in your B4A App
That's the general plan but using FCM.. when a device with my app detects a new earthquake from a third party RSS it sends a message request to FCM which sends the message to other devices subscribed to the same topic. This means most other devices with my app don't have to bother the RSS server, and users get their earthquake notifications faster..

It works well with the legacy HTTP version of FCM but I'm having problems with the new HTTP v1 API... At the moment, I tend to think that the B4A FCM class referenced in my top post needs updating, but for now I'm trying to see if aeric's work-around can achieve the result I'm after.

Sending notifications from my server is another issue which I'm trying to avoid by using FCM...
 
Upvote 0

GraemeW

Member
Licensed User
Longtime User
As mentioned by DonManfred, you should send the notification from a server.
What I can think of is to create a B4J server (or PHP web service) to handle the request.
Example is a REST API server which accept GET/POST or just a simple PHP page that trigger the CURL to send the push notification.
If you want to use an Android as a Http server, I worry it is not going to active 24/7 to standby and accept any request anytime.
Thanks aeric

Hmmm.. I'll have to think about this..

Currently, my app has a service which runs every 5 minutes to check a third-party RSS to see if a new earthquake has been reported. If yes, then the service sends a message request to FCM which sends a message with data about the new earthquake to all other devices with my app subscribed to the same topic. This means users receive the earthquake info quickly, and other devices don't have to query the RSS server. I'm aiming to increase the servce check time to 30 minutes and reduce the number of devices that actually query the RSS server to fewer than 10 % of installations.

This scheme currently works with legacy HTTP and should work with the new HTTP v1 using service account credentials to get the oauth2 token.. However I can't figure out how to get B4A to process the service account credentials.

Setting up a B4J server with your code seems like a backup solution if all else fails.. I can see how it might work but I'll have to figure out how to do it.. ?
 
Upvote 0

GraemeW

Member
Licensed User
Longtime User
OK - re-reading your kind responses, Erel's original library notes and the Google info it looks like I must route the app messages through a server B4J app to FCM, rather than direct from my Android app to FCM - in order to avoid having the FCM API keys in my Android app. This is new territory for me so we'll see how it goes over the next few days... :oops:
 
Upvote 0

GraemeW

Member
Licensed User
Longtime User
So, there are three main strategies

1) write a B4J server app based on aeric's example - but it needs to be hosted in node.js either by my server provider (VPS = significant extra cost) or by Google Cloud (small extra cost plus significant brain confusion)

2) use Firebase Realtime Database with triggered FCM messages (also complex for me)

3) adapt a php script from someone else, which I can easily install on my provider's server (zero extra cost and I've done it before for SQL database processing)

For now I'll work on 3)...
 
Upvote 0

GraemeW

Member
Licensed User
Longtime User
Well, that wasn't too bad. This very basic php server script forwards a message from the first device which detects a new earthquake to all the other devices. It achieves my original aim and moves the api key out of my app to a server, which should satisfy the Play Store gnomes. It's essentially a php version of Erel's original B4J-sendingtool in FirebaseNotifications - Push messages / Firebase Cloud Messaging (FCM). Hopefully problem solved.. ?
PHP:
<?php

//receives a topic+title+body message from an Android app and forwards it to Firebase Cloud Messaging
//FCM sends the message to other Android devices which are subscribed to the topic

//body is a JSON string - the sending service of the Android app on the originating device handles its composition and the receiving service in the apps on multiple receiving devices handle decomposition

//uses FCM Legacy HTTP and Cloud Messaging API (Legacy) Server key

//forwarded messages are currently structured for Android notifications (will use separate topic with "ios_"&topic to identify notifications for ios devices later)
 
//Call .php?tp=topic&t=title&b=body

$tp = $_GET["tp"];

$t = $_GET["t"];

$b = $_GET["b"];

define( 'API_KEY', 'AAA.............' );

$headers = array
(
  'Authorization: key=' . API_KEY,
  'Content-Type: application/json;charset=UTF-8'
);

$msg = array
(
  'title' => $t,
  'body' => $b,
);

$fields = array
(
  'to' => '/topics/' . $tp,
  'priority' => 10,
  'data' => $msg
);


//forward message

$ch = curl_init();
curl_setopt( $ch,CURLOPT_URL, 'https://fcm.googleapis.com/fcm/send' );
curl_setopt( $ch,CURLOPT_POST, true );
curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );
curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );
$result = curl_exec($ch );
curl_close( $ch );
return $result;

?>
 
Upvote 0
Solution

aeric

Expert
Licensed User
Longtime User
Well, that wasn't too bad. This very basic php server script forwards a message from the first device which detects a new earthquake to all the other devices. It achieves my original aim and moves the api key out of my app to a server, which should satisfy the Play Store gnomes. It's essentially a php version of Erel's original B4J-sendingtool in FirebaseNotifications - Push messages / Firebase Cloud Messaging (FCM). Hopefully problem solved.. ?
PHP:
<?php

//receives a topic+title+body message from an Android app and forwards it to Firebase Cloud Messaging
//FCM sends the message to other Android devices which are subscribed to the topic

//body is a JSON string - the sending service of the Android app on the originating device handles its composition and the receiving service in the apps on multiple receiving devices handle decomposition

//uses FCM Legacy HTTP and Cloud Messaging API (Legacy) Server key

//forwarded messages are currently structured for Android notifications (will use separate topic with "ios_"&topic to identify notifications for ios devices later)
 
//Call .php?tp=topic&t=title&b=body

$tp = $_GET["tp"];

$t = $_GET["t"];

$b = $_GET["b"];

define( 'API_KEY', 'AAA.............' );

$headers = array
(
  'Authorization: key=' . API_KEY,
  'Content-Type: application/json;charset=UTF-8'
);

$msg = array
(
  'title' => $t,
  'body' => $b,
);

$fields = array
(
  'to' => '/topics/' . $tp,
  'priority' => 10,
  'data' => $msg
);


//forward message

$ch = curl_init();
curl_setopt( $ch,CURLOPT_URL, 'https://fcm.googleapis.com/fcm/send' );
curl_setopt( $ch,CURLOPT_POST, true );
curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );
curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );
$result = curl_exec($ch );
curl_close( $ch );
return $result;

?>
This is exactly what I am using but while it is still working for my old app but isn't this using the legacy HTTP API?
 
Upvote 0

GraemeW

Member
Licensed User
Longtime User
This is exactly what I am using but while it is still working for my old app but isn't this using the legacy HTTP API?
Yes it is ... to use the V1 API I'd have to work out how to include a GoogleCredentials object in my php script on a hosted linux server. This is out of my league. For now legacy HTTP API works..
 
Upvote 0
Top