Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling refresh tokens for FCM device groups

I am trying to implement Firebase cloud messaging in my Android app through a Node.js server and I have got stuck at a usecase.

I saw the Firebase tutorial of creating a device group using registration tokens to send messages/notifications to all devices with the same user logged in, what I don't understand is what happens when one of the registration tokens get refreshed by onTokenRefresh() method.

How will I distinguish which token to change as all will be belonging to the same user?

Update:

Ok, so now I have got stucked on another blocking use case. I am creating a user group identified by the user id from my server. If user uninstalls and reinstalls the app immediately and another user logs in on the device, if I call a gcm message on the previous user group this device still receives it.

Is there any way for the gcm to identify is the device it is sending the notification to is logged in or not and if it is, is it logged in with the same user as for the group?

like image 282
Parichit Avatar asked Oct 19 '16 19:10

Parichit


2 Answers

There is another way to solve this problem using Cloud Firebase Functions.

How will I distinguish which token to change as all will be belonging to the same user?

Using Firebase Functions, you don't have to. Within onTokenRefresh(), you send the new token to the server.

For Example:

The user has 3 devices, each of which have a token that has been sent to server.

*** deviceTokenA/B/C represent UIDs of the token ... we do not know what they are, or which device they belong to.

UserId:
    Device Tokens:
        deviceTokenA: true,
        deviceTokenB: true,
        deviceTokenC: true,

Now, the User is on the device that triggered deviceTokenA. The token is refreshed, and onTokenRefresh() is called, sending the token to that collection.

onTokenRefresh() {
    String refreshedToken = FirebaseInstanceId.getInstance().getToken();
    sendTokenToServer(refreshedToken);
}

sendTokenToServer(String refreshedToken) {
    // send to Firebase or Firestore Database, put in the Device_Tokens collection.  }

Now, you will have 4 tokens in the collection.

 UserId:
        Device Tokens:
            deviceTokenA: true,  // this one has been "unregistered"
            deviceTokenB: true,
            deviceTokenC: true,
            deviceTokenD: true, // this one has been registered.

The deviceTokenA no longer applies, because it was refreshed, and is not attached to an actual device anymore.

When looking at the device Tokens, we still don't know which ones are good, which are bad, and which tokens belong to which device. That's ok!

So, then create a forEach loop, getting each Token, and then send an FCM to each of these Tokens, FCM can let us know which tokens were sent successfully. One of them will return an error. If it returns an error saying the token was bad, we can then catch the error and delete that token, so it will not be called again.

// outside for Each loop
     var promiseHolder = [];


// create a forEach loop, iterating through the collection of deviceTokens
// within that loop, put:

let innerPromise = admin.messaging().send(message)
.then(response => {
    console.log('notification sent success: ' + response);
})
.catch((error) => {
    console.log('Error sending notification: ' + error);

    // if the error == bad token message, then Delete the Token.
    if (error == 'Error: Requested entity was not found.') {
        console.log('you matched the error, token doesn't work, handle here.');

        //delete the old token
        return admin.firestore()doc(`users/${userID}/device_tokens/${token_id}`).delete();
     }
}

// still within forEach loop
promiseHolder.push(innerPromise);

// end the forEach Loop, and outside forEachLoop put:
return Promise.all(promiseHolder);
like image 144
Jeff Padgett Avatar answered Nov 14 '22 05:11

Jeff Padgett


So I've been thinking about how to go with this scenario. First off, let's put in the instances when onRefreshToken() is called:

This will not be called very frequently, it is needed for key rotation and to handle Instance ID changes due to:

  • App deletes Instance ID
  • App is restored on a new device
  • User uninstalls/reinstall the app
  • User clears app data

Guess with that, you can say that 'onTokenRefresh()` will be called after one the above happens and if the device is online (of course it has to be online on order to get a new token). So I guess here's how I'd go on the scenario:

First off, upon registration, I would save the registration token and pair it along another identifier, let's say a deviceId (since we're in a scenario for a user with multiple devices) in my App Server.

So assume I add in 3 registration tokens, those are also paired with their deviceIds. I add them all to a device group.

Now say one of the devices triggers the onTokenRefresh(), I would immediately send a delete request to my App Server for the registration token that is currently paired to that deviceId (you should also delete it in any device group(s) it's connected to), replacing it with the new one, then re-add it to the corresponding device group(s).

That's the simplest way I can think of. The key here is for you to pair the registration tokens with another identifier and use it to find which registration token you need to replace.

like image 5
AL. Avatar answered Nov 14 '22 03:11

AL.