tmolitor-stud-tu / mod_push_appserver

Simple and extendable appserver for XMPP pushes (aka. XEP-0357)
MIT License
25 stars 9 forks source link

FCM: Handling of NotRegistered errors #19

Closed marc0s closed 5 years ago

marc0s commented 5 years ago

Hi, as stated in https://firebase.google.com/docs/cloud-messaging/http-server-ref#table9 whenever a NotRegistered error from FCM is received, app server should drop the failing token and never use it again to send push notifications.

I have added such a feature by firing unregister-push-token inside the fcm_handler if such an error is received. See diff below.

The problem with this patch is that mod_push_appserver keeps the information about whether the registration works ok or not by setting last_successful_push or last_push_error accordingly and this undoes the deleting of the registration just done in fcm_handler.

I'm not sure how to tackle this. Should the return value of the fcm_handler provide more information on what to do? Or maybe just because an error happened, this token should not be used anymore even if it's present in the storage?

diff --git a/mod_push_appserver_fcm/mod_push_appserver_fcm.lua b/mod_push_appserver_fcm/mod_push_appserver_fcm.lua
index 343dbcd..749ac2c 100644
--- a/mod_push_appserver_fcm/mod_push_appserver_fcm.lua
+++ b/mod_push_appserver_fcm/mod_push_appserver_fcm.lua
@@ -112,6 +112,10 @@ local function fcm_handler(event)
                        if result.error and #result.error then
                                module:log("error", "Got FCM error:", result.error);
                                fcm_error = tostring(result.error);             -- return last error to mod_push_appserver
+                               if result.error == "NotRegistered" then
+                                       module:log("warn", "Unregistering failing FCM token %s", tostring(settings["token"]))
+                                       module:fire_event("unregister-push-token", {token = tostring(settings["token"]), type = "fcm"})
+                               end
                        end
                end
                return fcm_error;

Thanks!

PurnimaNaik commented 5 years ago

I was facing the same issue. Here is how I fixed it (iOS specific, android was working out of the box)-

  1. Uninstall the app from your phone
  2. Clear ALL the tokens stored in your database for this particular account(or however you have set it up in your backend. In my case, tokens are tied to the account)
  3. In componentDidmount-
      async componentDidMount() {
        this.checkPermission();
        this.createNotificationListeners();
      }

4.

  async checkPermission() {
    const enabled = await firebase.messaging().hasPermission();
    if (enabled) {
      this.getToken();
    } else {
      this.requestPermission();
    }
  }

  async requestPermission() {
    try {
      await firebase.messaging().requestPermission();
      // User has authorised
      this.getToken();
    } catch (error) {
      // User has rejected permissions
      console.log('permission rejected');
    }
  }

  async getToken() {
    try {
      const enabled = await firebase.messaging().hasPermission();
      if (!enabled) {
        await firebase.messaging().requestPermission();
      }

      const fcmToken = await firebase.messaging().getToken();
      if (fcmToken) {
        console.log("got token");
        console.log('fcm token:', fcmToken); //-->use this token from the console to send a post request via postman
        this.setState({ fcmToken });
        return fcmToken;
      }
    } catch (error) {
      console.warn('notification token error', error);
    }
  }

5.

 async createNotificationListeners() {
    /*
    * Triggered when a particular notification has been received in foreground
    * */
    this.notificationListener = firebase.notifications().onNotification((notification) => {
        const { title, body } = notification;
        this.showAlert(title, body);
    });

    /*
    * If your app is in background, you can listen for when a notification is clicked / tapped / opened as follows:
    * */
    this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen) => {
        const { title, body } = notificationOpen.notification;
        this.showAlert(title, body);
    });

    /*
    * If your app is closed, you can check if it was opened by a notification being clicked / tapped / opened as follows:
    * */
    const notificationOpen = await firebase.notifications().getInitialNotification();
    if (notificationOpen) {
        const { title, body } = notificationOpen.notification;
        this.showAlert(title, body);
    }
    /*
    * Triggered for data only payload in foreground
    * */
    this.messageListener = firebase.messaging().onMessage((message) => {
      //process data message
      console.log(JSON.stringify(message));
    });
  }
  1. Finally, use the token that you see in the console as a value for the "to" key in your post request. You should no longer see the "notRegistered" error and should be getting push notifications. Done!!

For a more detailed setup/citation- https://medium.com/@anum.amin/react-native-integrating-push-notifications-using-fcm-349fff071591