InstanceID returns invalid token - android

I'm using GCM on Android.
I use InstanceID.getInstance(...).getToken(...) to receive a push token, but in some cases (after application updates or re-install) I receive an invalid token.
When the server returns a NotRegistered error, I've connected with a debugger and called InstanceID.getInstance(...).getToken(...). But this token is not valid (I've tried to send push via curl -s "https://android.googleapis.com/gcm/send" ... using this token), I receive NotRegistered error.
Why instanceID could return invalid token?

It shouldn't give invalid token.
Have you applied all procedures?
InstanceIDListenerService: When the token changes via the app
updates, etc.
RegistrationIntentService: When the token changes, you receive it via InstanceIDListenerService and call this intent to get a new
token.

Finally, I found a solution.
I worked with instanceID from two different threads. I called getToken(...) two times simultaneously. If instance doesn't have a cache, it get token from network. I think, it sends two requests in my case and there is no guarantee of it's order. So instanceID cached one token, but google cloud another one.

Related

Firebase Cloud Messaging : Expiration of FCM token

I understand that the FCM token renews itself if one of the following happens.
-The app deletes Instance ID
-The app is restored on a new device
-The user uninstalls/reinstall the app
-The user clears app data.
The following can be used at the App side to monitor Token renewal.
Monitor token generation
The onTokenRefreshcallback fires whenever a new token is generated, so
calling getToken in its context ensures that you are accessing a
current, available registration token. Make sure you have added the
service to your manifest, then call getToken in the context of
onTokenRefresh, and log the value as shown:
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(refreshedToken);
}
My question is, when the app is terminated, the token expires and there is no way for the FCM server to know what the new token is for the device (if exist). So when I send a notification/data message to this device, the server fails to send it to the device as it doesn't know where to send it to (as there is no valid token). How do I make sure that in such situations I can notify the device ? I dont do a customer token generation. So the it seems to refresh the token now and then. How do I increase the validity of my token ?
You will need to check for an error when sending the message, and pay attention to the error codes, as listed in the documentation. You should stop using the token if you get the error messaging/registration-token-not-registered.

FCM TOPIC Creation in APP Server

Recently I migrated from GCM to FCM for sending the notification via my app, i want to know whether can able to subscribe the members in particular topic from my app server. If possible, then how will find out if a particular member token valid or expired.
Because in my database, i have near to 22L people GCM Registration TOKEN id, so that i will create one topic and subscribe those members via my app server.
Any ideas to resolve this kind of issues.
You can subscribe multiple tokens to Topic via your App Server using the Instance ID API, specifically, using batchAdd. It can also identify if the Registration Token that you were subscribing is invalid by returning a NOT_FOUND error. From the docs:
Manage relationship maps for multiple app instances
Using the Instance ID service's batch methods, you can perform batch management of app instances. For example, you can perform bulk addition or removal of app instances to an FCM or GCM topic. To manage app instances, call the Instance ID service at this endpoint, providing the app instance tokens in the JSON body:
https://iid.googleapis.com/iid/v1:batchAdd
https://iid.googleapis.com/iid/v1:batchRemove
Parameters
Authorization: key=YOUR_API_KEY. Set this parameter in the header.
to : The topic name.
registration_tokens : The array of IID tokens for the app instances you want to add or remove.
Results
On success the call returns HTTP status 200. Empty results indicate successful subscription for the token. For failed subscriptions, the result contains one of these error codes:
NOT_FOUND — The registration token has been deleted or the app has been uninstalled.
INVALID_ARGUMENT — The registration token provided is not valid for the Sender ID.
INTERNAL — The backend server failed for unknown reasons. Retry the request.
TOO_MANY_TOPICS — Excessive number of topics per app instance.
Example POST request
https://iid.googleapis.com/iid/v1:batchAdd
Content-Type:application/json
Authorization:key=API_KEY
{
"to": "/topics/movies",
"registration_tokens": ["nKctODamlM4:CKrh_PC8kIb7O...", "1uoasi24:9jsjwuw...", "798aywu:cba420..."],
}
Example result
HTTP 200 OK
{
"results":[
{},
{"error":"NOT_FOUND"},
{},
]
}

FirebaseInstanceId.getInstance().getToken() without using Default FirebaseApp

I´m trying to use FCM to send Firebase Notifications, but I have a Application using one Firebase Project and a library using another Firebase Project. I want to receive the token from library Firebase Project to receive Firebase Notifications.
When I try directly receive the token using:
FirebaseInstanceId.getInstance().getToken();
I received a valid token, but from Application Firebase Project. If i try to force in getInstance() the library Firebase Project, using:
FirebaseInstanceId.getInstance(FirebaseApp.getInstance("ABC")).getToken();
I receive null. If I try to use Application or Library Firebase Project passing parameters in getToken() as:
FirebaseInstanceId.getInstance().getToken(getApplication().getResources().getString(R.string.gcm_defaultSenderId), FirebaseMessaging.INSTANCE_ID_SCOPE);
or
FirebaseInstanceId.getInstance(FirebaseApp.getInstance("ABC")).getToken(getApplication().getResources().getString(R.string.gcm_defaultSenderId), FirebaseMessaging.INSTANCE_ID_SCOPE);
I received the same valid token from Application Firebase Project.
the R.string.gcm_defaultSenderId is from library project resources
There is another way to receive Library Firebase Project token or I'm doing something wrong?
This is my understanding of what you are observing based on my experiments and the documentation.
When you first call getToken() using a FirebaseApp other then the default, there is no token and communication with the server is required to produce one. A null value is returned and the process to fetch a token is initiated. The documentation for getToken() says it returns "the master token or null if the token is not yet available". After a few seconds the token is received. You can detect that event using the onTokenRefresh() method of FirebaseInstanceIdService, if you have implemented that. On subsequent calls to getToken() for the non-default app, the token will be present and returned immediately by getToken().
I think the calls to get a scoped token always return a token because they are blocking (see docs), and wait for the interaction with the server to complete before returning a result.
I made a mistake in FirebaseApp.initializeApp(), because I forgot to set:
.setGcmSenderId(getApplication().getResources().getString(R.string.gcm_defaultSenderId))
in FirebaseOptions.Builder().
After that change, I receive a valid Library Firebase Token in getToken() and in onTokenRefresh()

Why InstanceID always returns a token despite of wrong senderId?

I'm using Google Cloud Messaging api to implement an Android client. To get a token, I do:
InstanceID ex = InstanceID.getInstance(this.getApplicationContext());
String regId = ex.getToken(senderId, "GCM", null);
And regId always contains a token, despite of I put on senderId. If I set senderId with "123", InstanceID returns a token! (But the, I don't receive notifications with this token). Why I always get a token? I expect an exception, or a null value maybe...
You may always get a token but you will not be able to use it to establish the full gcm lifecycle since instanceId is tied to your app server's senderId.
From the google documentation
Use the getToken method to prove the ownership of the InstanceID and
to allow servers to access data or services associated with the app.
The method follows the patterns of OAuth2, and requires an
authorizedEntity and scope. The authorizedEntity can be a project ID
or another InstanceID, and it determines the services that are
authorized to use the generated token. The scope determines the
specific service or data to which the token allows access.
To understand more of the lifecycle, refer to the documentation

Correct use and update of GCM-Ids

I have a question about the correct use of GCM-IDs.
At the moment I have a InstanceIDListenerService a GcmListenerService and a RegestrationIntentService.
The RegeistrationIntentServie get started in the MainActivity every time someone opens the app.
I think this is a correct implementation of the Google guidelines.
But what is the best way to handle the GCM-ID so that I will not have incorrect GCM-IDs on my server after the refresh in the InstandeIDListenerService. Because at the moment the refresh only registers a new GCM-ID on my server.
Would it be an idea to generate a random Device ID so that I can update the old GCM-ID?
How do you handle the IDs?
Because at the moment I ask the server every start of the app if he knows a GCM-ID in combination with a (randomly generated) Device-ID and update one of both if the other one is incorrect or does not exist.
And I think that produces a lot of network traffic for nothing.
Just to be sure, you are not calling InstanceIDListenerService.onTokenRefresh() yourself right? This will be called by the system if necessary.
To answer your question, you should use the GCM functionality called "canonical IDs":
Canonical IDs
If a bug in the client app triggers multiple registrations for the
same device, it can be hard to reconcile state and the client app
might end up with duplicate messages.
Implementing canonical IDs can help you more easily recover from these
situations. A canonical registration ID is the registration token of
the last registration requested by the client app. This is the ID
that the server should use when sending messages to the device.
If you try to send a message using an old registration token, GCM will
process the request as usual, but it will include the canonical ID in
the registration_id field of the response. Make sure to replace the
registration token stored in your server with this canonical ID, as
eventually the old registration token will stop working.
To be more precise, if there are two registrations for the same device and you send a notification using the older registration ID, you will get the canonical ID (the registration ID of the newest registration for this device). If this ID is already stored on your server, delete the old registration. If the canonical ID is not stored on your server for any reason, replace the registration ID you used to send the notification with the canonical ID.
#leet GCM ID token initiates callback periodically when your token needs to be refresh, or sometime when:
- Security issues; like ssl or platform issues
- Device info is no longer valid; for example backup and restore.
- Instance ID service is otherwise affected.
public class MyInstanceIDService extends InstanceIDListenerService {
public void onTokenRefresh() {
refreshAllTokens();
}
private void refreshAllTokens() {
// assuming you have defined TokenList as
// some generalized store for your tokens
ArrayList<TokenList> tokenList = TokensList.get();
InstanceID iid = InstanceID.getInstance(this);
for(tokenItem : tokenList) {
tokenItem.token =
iid.getToken(tokenItem.authorizedEntity,tokenItem.scope,tokenItem.options);
// send this tokenItem.token to your server
}
}
};
Keeping the Registration State in
Sync
To protect the client app and app server from potential malicious
re-use of registration tokens, you should periodically initiate token
refresh from the server. When GCM registration token refresh is
initiated from server side, the client app must handle
a tokenRefreshed message with the GCM registration client/server
handshake. This link may help you verify on how
to handle Refresh Token.
As Baris have metioned, your server must use canonical IDs when you are sending message to the device. The tokenRefresh would remove the idea of generating a random device ID and Canonical IDs would solve on how you verify if the ID is correct or not.

Categories

Resources