I am aware of the ability to create a named FirebaseApp instance with custom options for use with, for example, FirebaseDatabase, but I specifically need the ability to use FirebaseMessagaging against different (determined at runtime) Firebase projects.
The problem I have is that FirebaseMessaging.getInstance() doesn't support the name argument.
Is this in any way possible?
The reason I need to support separate projects is that our client connects to different customers server, so each customer will have their own firebase account and be generating their own notifications.
If it is not possible, is there any way to isolate one customer from another, so they can't possibly send notifications to another customer's device (assuming they could obtain a valid target device token in some way)?
The Firebase Blog contains a post about this exact issue, and provides a guide for how to handle it.
The blog post describes two steps:
Disable FirebaseInitProvider
Add this to the AndroidManifest.xml to disable the default initialization code:
<provider
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:authorities="${applicationId}.firebaseinitprovider"
tools:node="remove"
/>
Call FirebaseApp.initializeApp(Context, FirebaseOptions)
As soon as you have the necessary configuration ready, initialize Firebase on your own with:
FirebaseOptions.Builder builder = new FirebaseOptions.Builder()
.setApplicationId("1:0123456789012:android:0123456789abcdef")
.setApiKey("your_api_key")
.setDatabaseUrl("https://your-app.firebaseio.com")
.setStorageBucket("your-app.appspot.com");
FirebaseApp.initializeApp(this, builder.build());
The Google Services Plugin documentation describes how to get the values you need from the JSON:
{YOUR_CLIENT} is the client object that contains a client_info/android_client_info/package_name that matches your package name (application id).
google_app_id: {YOUR_CLIENT}/client_info/mobilesdk_app_id
gcm_defaultSenderId: project_info/project_number
default_web_client_id: {YOUR_CLIENT}/oauth_client/client_id (client_type == 3)
ga_trackingId: {YOUR_CLIENT}/services/analytics-service/analytics_property/tracking_id
firebase_database_url: project_info/firebase_url
google_api_key: {YOUR_CLIENT}/api_key/current_key
google_crash_reporting_api_key: {YOUR_CLIENT}/api_key/current_key
To allow a single client to receive messages from multiple senders you can use the FirebaseInstanceId.getToken(A_SENDER_ID, "FCM") method. So there is no need to have retrieve multiple Messaging instances, to manage multiple senders.
Once the message arrives you can filter on the from field of the RemoteMessage object that is passed via the onMessageReceived callback.
If you navigate to FirebaseMessaging class, you'll see there's a package private getInstance which takes a firebaseApp. Also you'll see getInstance() is also using that with the default firebase app as the parameter. You can get it by reflection.
val firebaseApp = buildFirebaseApp()
val method = FirebaseMessaging::class.java.getDeclaredMethod("getInstance", FirebaseApp::class.java)
// Set the non-public accessible if security allowed
method.isAccessible = true
val fcmMessaging = method.invoke(null, firebaseApp) as FirebaseMessaging
Related
I am trying to implement SafetyNet in my app. I also, don't have a server, and I am using Firebase Firestore and Firebase Functions.
My knowledge about Firebase Functions is very limited. And I was wondering if I could somehow use the functions to help me with the SafetyNet attestation. As I see, I should be producing a nonce on the cloud, send this nonce to the app, use it to attest, and send it back to the cloud to verify the integrity correct?
But I can't seem to find anywhere on how to do this. Can anyone point me in the right direction?
YES
Sorry for the excitement there, but this is possible since a few weeks ago through a new feature called Firebase App Check.
With App Check, you always end up with a two-step process:
Use an attestation provider (such as SafetyNet) in your application, so that information about the app is attached to each request it makes to Firebase.
Then at some point in time, when enough of your app requests have this information attached, check for the app information in Cloud Functions, or enable the check in one of the other supported services.
If you check the documentation on enabling App Check enforcement for Cloud Functions, you'll see that it mostly boils down to this check in the code:
exports.yourCallableFunction = functions.https.onCall((data, context) => {
// context.app will be undefined if the request doesn't include a valid
// App Check token.
if (context.app == undefined) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called from an App Check verified app.')
}
// Your function logic follows.
});
I want to be able to add more than one sender id in my android app.
From https://developers.google.com/cloud-messaging/concept-options
GCM allows multiple parties to send messages to the same client app. For example, suppose the client app is an articles aggregator with multiple contributors, and each of them should be able to send a message when they publish a new article. This message might contain a URL so that the client app can download the article. Instead of having to centralize all sending activity in one location, GCM gives you the ability to let each of these contributors send its own messages.
How is this achieved using google-services.json configuration file?
UPDATE: Going to refer to the official and recommended way in doing this instead of the hacky and unofficial approach to prevent/avoid unknown problems. From my answer here.
There is actually a part in the documentation about this topic:
Receiving messages from multiple senders
FCM allows multiple parties to send messages to the same client app. For example, suppose the client app is an article aggregator with multiple contributors, and each of them should be able to send a message when they publish a new article. This message might contain a URL so that the client app can download the article. Instead of having to centralize all sending activity in one location, FCM gives you the ability to let each of these contributors send its own messages.
To make this possible, make sure each sender generates its own sender ID. See the client documentation for your platform for information on on how to obtain the FCM sender ID. When requesting registration, the client app fetches the token multiple times, each time with a different sender ID in audience field.
Finally, share the registration token with the corresponding app servers (to complete the FCM registration client/server handshake), and they'll be able to send messages to the client app using their own authentication keys.
Note that there is limit of 100 multiple senders.
I think the confusing but important part here is:
When requesting registration, the client app fetches the token multiple times, each time with a different sender ID in audience field.
In other terms, you'll have to call getToken() passing the Sender ID and simply "FCM" (e.g. getToken("2xxxxx3344", "FCM")) as the parameters. You'll have to make sure that you call this for each sender (project) that you need.
Also, note from the getToken() docs:
This is a blocking function so do not call it on the main thread.
Some additional good-to-knows:
It does not auto retry if it fails like the default one.
It returns an IOException when it fails.
As of Dec. 2016, there's a very simple, non-hacky way to do this, which still works now (Jul 2018).
FirebaseOptions options = new FirebaseOptions.Builder()
.setApplicationId("1:something:android:something_else") // Required for Analytics.
.setApiKey("your apikey") // Required for Auth.
.setDatabaseUrl("https://your-database.firebaseio.com/") // Required for RTDB.
.build();
FirebaseApp.initializeApp(this /* Context */, options, "secondary");
Source: The official Firebase blog
Comma seperated senderID solution is still working and able to register same token for 2 different sender. I sent push notif to that single magical token with using 2 different api key and able to receive push notifs for both api key. Hope it works at least till the end of 2020. Because I'm trying to make a seamless transition between an old GCM and FCM projects which targets more than 1 million user. (hear me google and thank you google for not deprecating this great solution)
String magicalToken = FirebaseInstanceId.getInstance().getToken("senderId, anotherSenderId", "FCM");
You can get the single token for multiple sender by passing them as comma separated string and then these sender will be able to send the push notification using the common token, try calling
FirebaseInstanceId.getInstance() .getToken("senderId1,senderId2",
FirebaseMessaging.INSTANCE_ID_SCOPE);
make sure you call this from a background thread.
I want to be able to add more than one sender id in my android app.
From https://developers.google.com/cloud-messaging/concept-options
GCM allows multiple parties to send messages to the same client app. For example, suppose the client app is an articles aggregator with multiple contributors, and each of them should be able to send a message when they publish a new article. This message might contain a URL so that the client app can download the article. Instead of having to centralize all sending activity in one location, GCM gives you the ability to let each of these contributors send its own messages.
How is this achieved using google-services.json configuration file?
UPDATE: Going to refer to the official and recommended way in doing this instead of the hacky and unofficial approach to prevent/avoid unknown problems. From my answer here.
There is actually a part in the documentation about this topic:
Receiving messages from multiple senders
FCM allows multiple parties to send messages to the same client app. For example, suppose the client app is an article aggregator with multiple contributors, and each of them should be able to send a message when they publish a new article. This message might contain a URL so that the client app can download the article. Instead of having to centralize all sending activity in one location, FCM gives you the ability to let each of these contributors send its own messages.
To make this possible, make sure each sender generates its own sender ID. See the client documentation for your platform for information on on how to obtain the FCM sender ID. When requesting registration, the client app fetches the token multiple times, each time with a different sender ID in audience field.
Finally, share the registration token with the corresponding app servers (to complete the FCM registration client/server handshake), and they'll be able to send messages to the client app using their own authentication keys.
Note that there is limit of 100 multiple senders.
I think the confusing but important part here is:
When requesting registration, the client app fetches the token multiple times, each time with a different sender ID in audience field.
In other terms, you'll have to call getToken() passing the Sender ID and simply "FCM" (e.g. getToken("2xxxxx3344", "FCM")) as the parameters. You'll have to make sure that you call this for each sender (project) that you need.
Also, note from the getToken() docs:
This is a blocking function so do not call it on the main thread.
Some additional good-to-knows:
It does not auto retry if it fails like the default one.
It returns an IOException when it fails.
As of Dec. 2016, there's a very simple, non-hacky way to do this, which still works now (Jul 2018).
FirebaseOptions options = new FirebaseOptions.Builder()
.setApplicationId("1:something:android:something_else") // Required for Analytics.
.setApiKey("your apikey") // Required for Auth.
.setDatabaseUrl("https://your-database.firebaseio.com/") // Required for RTDB.
.build();
FirebaseApp.initializeApp(this /* Context */, options, "secondary");
Source: The official Firebase blog
Comma seperated senderID solution is still working and able to register same token for 2 different sender. I sent push notif to that single magical token with using 2 different api key and able to receive push notifs for both api key. Hope it works at least till the end of 2020. Because I'm trying to make a seamless transition between an old GCM and FCM projects which targets more than 1 million user. (hear me google and thank you google for not deprecating this great solution)
String magicalToken = FirebaseInstanceId.getInstance().getToken("senderId, anotherSenderId", "FCM");
You can get the single token for multiple sender by passing them as comma separated string and then these sender will be able to send the push notification using the common token, try calling
FirebaseInstanceId.getInstance() .getToken("senderId1,senderId2",
FirebaseMessaging.INSTANCE_ID_SCOPE);
make sure you call this from a background thread.
Using the Mobile Backend Starter (MBS) Android classes (those distributed as a sample project when creating a new project in Google Dev Console and demoed at Google I/O 2013) I'm able to insert new entities to the cloud datastore via calls to CloudBackendMessaging.insertAll or .updateAll. The latter will create entities if none exist so seems functionally identical to insert for new records.
The insertion/creation works fine. However when I attempt to update existing entries in the datastore, I received permissions errors e.g. (from the backend log)
Method: mobilebackend.endpointV1.updateAll
Error Code: 401
Reason: required
Message: Insuffient permission for updating a CloudEntity: XXXXXX by: USER: YYYYYYY
which results in a matching access error in the logcat client side.
In all cases I am using Secured access authenticating with a valid Google account (my own).
The entities being inserted are thus showing as "owned" by my user ID with "updated by" and "created by" showing my Google account's email address.
However when the update of the existing record is made, using exactly the same CloudBackendMessenger object and thus same credentials etc. the backend is telling me I can't update due to permissions issues. But surely if I just made the entity with the same credentials this can't be correct? Looking at the documentation it appears that I should be able to edit entities owned by the same user ID in all cases (regardless of the KindName and whether it is prepended [public], [private] or nothing).
Can anyone who has received permissions errors on UPDATES via Mobile Backend Starter for Datascore please shed any light? I have been banging my head over this for most of today.
I've faced the similar error "Insuffient permission for updating a CloudEntity" when using cloudBackendAsync.update(cloudEntity). I resolved it by making sure the cloudEntity has it's createdAt field set. createdAt is autogenerated and I think I am not supposed to touch it. But it worked for me. In my case I am first obtaining list of cloud entities. This is when I get createdAt field of cloud entities. Then when I am updating I setting the createdAt field from previously obtained entities.
Edit: Had to do similar thing for owner field also.
Similar to one of the comments above, I successfully got around this by getting the original CloudEntity before doing the insert/update/delete function.
CloudQuery cq = new CloudQuery("datastoretype");
cq.setLimit(1);
cq.setFilter(Filter.eq("_id",id));
cloudEntity.setId(id);
mProcessingFragment.getCloudBackend().get(cloudEntity, handler);
Thereafter it was trivial to do the following:
mProcessingFragment.getCloudBackend().update(cloudEntity, handler);
The docs definitely ought to be more clear on this, whether it is a strict requirement or bug.
The answers posted so far work around the problem if you don't mind all users being able to access the entity you are trying to update. However, a better solution that retains the access permissions is detailed by google here - https://cloud.google.com/cloud/samples/mbs/authentication
If you want to pass the user’s Google Account info to the backend on
each call, use the CloudBackend#setCredential() method (also available
on the subclasses, CloudBackendAsync and CloudBackendMessaging) to set
a GoogleAccountCredential object before calling any Mobile Backend
methods.
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this, "<Web Client ID>");
credential.setSelectedAccountName("<Google Account Name>");
cloudBackend.setCredential(credential);
Setting credientials enables the client to operate when the backend is
in “Secured by Client ID” mode and also sets createdBy/updatedBy/owner
properties of CloudEntity automatically.
What I'm trying to do is to authenticate my Android app to the Google Cloud Endpoint.
Basically the endpoints should only allow my Android app to access the methods and nothing else.
I have done these things -
Create a client id using my SHA1 value in Eclipse in the Google Cloud Console.
Create a web client id in the Google Cloud Console for my endpoint project.
Add both these client id's in the "#Api" mentioned on each endpoint.
Add an extra "user" parameter in the endpoint methods.
Regenerate and deploy the backend to the cloud.
But when I'm running this the "user" is always coming as "null". I'm at my wits end trying to find a proper working method for doing all this.
I've searched many forums but no proper answers anywhere.
Here's another similar post Restrict access to google cloud endpoints to Android app
This is the reference I'm using -
https://developers.google.com/appengine/docs/java/endpoints/auth
Has anyone here done this before? My main goal is to not allow unauthenticated apps and outside world to access the endpoints, for obvious security reasons. I don't want to use end-user based authentication since I want to keep my app very simple.
It sounds like it's working as intended. You control which client apps can call your endpoint methods via the client IDs as you have already done. The User parameter is coming in as null precisely because you aren't doing end-user authentication. The User parameter represents an actual real user (Google Account). So if you don't need end-user authenticated methods, you can just simply not define the User parameter, or else ignore the null value. You said your problem is that the User parameter is set null. What are you expecting it to be in this scenario?
You need to call authenticate on the client, then possibly the library you're using will 'inject' the user information.
Here's what worked for me :
Let's say you have the keys below :
static final String WEB_CLIENT_ID = "somekeyfor webclientid.apps.googleusercontent.com";
static final String ANDROID_CLIENT_ID = "somekeyfor androidclientid.apps.googleusercontent.com";
static final String ANDROID_AUDIENCE = WEB_CLIENT_ID;
Your Api anotation should look like this :
#Api(
name = "yourapiname",
clientIds = {CloudEndpoint.WEB_CLIENT_ID,CloudEndpoint.ANDROID_CLIENT_ID},
audiences = {CloudEndpoint.ANDROID_AUDIENCE},
version = "v1",
namespace = #ApiNamespace(
ownerDomain = "myapp.app.com",
ownerName = "myapp.app.com",
packagePath = ""
)
)
In the annotation below, notice how your audience is the variable --> ANDROID_AUDIENCE which is equal to WEB_CLIENT_ID.
Now in your app side, when you create the googleAccountCredential object, you should pass in the Web Client Id like this :
mAccountCredentials = GoogleAccountCredential.usingAudience(getApplicationContext(),"server:client_id:" + "yourwebclientID");
Note that even if this is properly done, your user object in the endpoint might still coming out as Null if the account name you pass in mAccountCredentials.setSelectedAccountName("accontname") does not exist in the device. Therefore make sure the account name you pass does exist in the Android device by going to --> (Settings/Accounts)