Firebase Cloud Functions Tokens

那年仲夏 提交于 2020-06-17 15:34:50

问题


I need help. Cloud functions written in node.js are throwing errors and not causing push notifications (alerter) to alert in my Android java app that a comment has been made or that a user has liked a post. On Firebase Console, I was able to get the error message in cloud functions log:

Successfully sent message: { results: [ { error: [Object] } ],
canonicalRegistrationTokenCount: 0,
failureCount: 1,
successCount: 0,
multicastId: 7952971403531609000 }

When I view the error message on Firebase Cloud Functions log, I also get the following:

Error: { Error: The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages.
at FirebaseMessagingError.Error (native)
at FirebaseMessagingError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseMessagingError (/user_code/node_modules/firebase-admin/lib/utils/error.js:253:16)
at Function.FirebaseMessagingError.fromServerError (/user_code/node_modules/firebase-admin/lib/utils/error.js:283:16)
at /user_code/node_modules/firebase-admin/lib/messaging/messaging.js:381:63
at Array.forEach (native)
at mapRawResponseToDevicesResponse (/user_code/node_modules/firebase-admin/lib/messaging/messaging.js:377:26)
at /user_code/node_modules/firebase-admin/lib/messaging/messaging.js:558:24
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
errorInfo:
{ code: 'messaging/registration-token-not-registered',
message: 'The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages.' },
codePrefix: 'messaging' }

The message is very clear: A previously valid registration token can be unregistered for a variety of reasons. I have seen other posts on these error topics but none outlines exactly how to treat and use tokens during registration and login.

Here is the Firebase Cloud Function Code ...

'use-strict'

const functions = require('firebase-functions');
const admin=require('firebase-admin');
admin.initializeApp(functions.config().firebase);


exports.sendCommentNotification=functions.firestore.document("Notifications/{user_id}/Comment/{comment_id}").onWrite((change,context)=> {

    const user_id=context.params.user_id;
    const comment_id=context.params.comment_id;

  console.log(user_id+":"+comment_id);

  return admin.firestore().collection("Notifications").doc(user_id).collection("Comment").doc(comment_id).get().then((queryResult)=>{

        const post_id=queryResult.data().post_id;
        const admin_user_id=queryResult.data().admin_id;
        const noti_id=queryResult.data().notification_id;
        const timestamp=queryResult.data().timestamp;    
        const post_desc=queryResult.data().post_desc;

        const admin_data=admin.firestore().collection("Users").doc(admin_user_id).get();
        const commenter_data=admin.firestore().collection("Users").doc(user_id).get();

        return Promise.all([commenter_data,admin_data]).then(result=>{

            const commenter_name=result[0].data().name;
            const commenter_image=result[0].data().image;
            const admin_token=result[1].data().token_id;
            const admin_name=result[1].data().name;

           if(commenter_name!=admin_name)
           {
               const payload={
               data:{
                notification_id:noti_id,
                timestamp:timestamp,
                post_id:post_id,
                admin_id:admin_user_id,
                title:commenter_name,
                from_image:commenter_image,

                post_desc:post_desc,
                body:"Commented on your post",
                click_action:"com.app.ej.ms.TARGET_COMMENT"
              }

            };


           admin.messaging().sendToDevice(admin_token,payload).then(function (response) {
            console.log("Successfully sent message:", response);
            return;
            })
            .catch(function (error) {
                console.log("Error sending message:", error);
            });


           }

          });
    });

});

...

Here is in the RegisterActivity.java

...

private void registerUser() {

        mAuth.createUserWithEmailAndPassword(email_, pass_).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull final Task<AuthResult> task) {
                if (task.isSuccessful()) {

                    Map<String,Object> usernameMap=new HashMap<String, Object>();
                    usernameMap.put("username",username_);

                    firebaseFirestore.collection("Usernames")
                            .document(username_)
                            .set(usernameMap)
                            .addOnSuccessListener(new OnSuccessListener<Void>() {
                                @Override
                                public void onSuccess(Void aVoid) {
                                    task.getResult()
                                            .getUser()
                                            .sendEmailVerification()
                                            .addOnSuccessListener(new OnSuccessListener<Void>() {
                                                @Override
                                                public void onSuccess(Void aVoid) {

                                                    final String userUid = task.getResult().getUser().getUid();
                                                    final StorageReference user_profile = storageReference.child(userUid + ".png");
                                                    user_profile.putFile(imageUri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
                                                        @Override
                                                        public void onComplete(@NonNull final Task<UploadTask.TaskSnapshot> task) {
                                                            if (task.isSuccessful()) {

                                                               user_profile.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
                                                                   @Override
                                                                   public void onSuccess(Uri uri) {

                                                                       //String token_id = FirebaseInstanceId.getInstance().getToken(); // Deprecated
                                                                       // TODO https://firebase.google.com/docs/cloud-messaging/android/client#retrieve-the-current-registration-token.
                                                                       // FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener( RegisterActivity.this,  new OnSuccessListener<InstanceIdResult>() {

                                                                       FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
                                                                           @Override
                                                                           public void onComplete(@NonNull Task<InstanceIdResult> task) {
                                                                               if (!task.isSuccessful()) {
                                                                                   Log.w(TAG, "getInstanceId failed", task.getException());
                                                                                   return;
                                                                               }
                                                                               // String token_id = instanceIdResult.getToken();
                                                                               String token_id = task.getResult().getToken();
                                                                               Log.i(TAG, "RegisterActivity Token ID (token_id): " + token_id);

                                                                               Map<String, Object> userMap = new HashMap<>();
                                                                               userMap.put("id", userUid);
userMap.put("name", name_);
userMap.put("image", uri.toString());
userMap.put("email", email_);
userMap.put("bio",getString(R.string.default_bio));
userMap.put("username", username_);
userMap.put("location", location_);
userMap.put("token_id", ""); //token_id

                           firebaseFirestore.collection("Users").document(userUid).set(userMap).addOnSuccessListener(new OnSuccessListener<Void>() {
                                                                                   @Override
                                                                                   public void onSuccess(Void aVoid) {
                                                                                       mDialog.dismiss();
                                                                                       Toast.makeText(RegisterActivity.this, "Verification email sent", Toast.LENGTH_SHORT).show();
                                                                                       finish();
                                                                                   } 

...

Here is in the LoginActivity.java

...

public void performLogin(final boolean override) {

        final String email_, pass_;
        email_ = email.getText().toString();
        pass_ = password.getText().toString();

        if (!TextUtils.isEmpty(email_) && !TextUtils.isEmpty(pass_)) {
            mDialog.show();

            mAuth.signInWithEmailAndPassword(email_, pass_).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull final Task<AuthResult> task) {
                    if (task.isSuccessful()) {

                        //Toast.makeText(LoginActivity.this, "Login Successful, continue to email verified", Toast.LENGTH_LONG).show();
                        Log.i(TAG, "Login Successful, continue to email verified");

                        if (task.getResult().getUser().isEmailVerified()) {

                            //Toast.makeText(LoginActivity.this, "Email is verified Successful, continue to get token", Toast.LENGTH_LONG).show();
                            Log.i(TAG, "Email is verified Successful, continue to get token");

                            //final String token_id = FirebaseInstanceId.getInstance().getToken(); Deprecated
                            // FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener( LoginActivity.this,  new OnSuccessListener<InstanceIdResult>() {
                            FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
                                @Override
                                public void onComplete(@NonNull Task<InstanceIdResult> task2) {
                                    if (!task2.isSuccessful()) {
                                        Log.w(TAG, "getInstanceId failed", task2.getException());
                                        return;
                                    }

                                    // Get new Instance ID token
                                    String token_id = task2.getResult().getToken();


                                    Log.i(TAG, "Get Token Listener, Token ID (token_id): " + token_id);

                                    final String current_id = task.getResult().getUser().getUid();

                                    mFirestore.collection("Users").document(current_id).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                                        @Override
                                        public void onSuccess(DocumentSnapshot documentSnapshot) {
                                            if (!override) {
                                                if (documentSnapshot.getString("token_id").equals(token_id) || documentSnapshot.getString("token_id").equals("")) {

                                                    Map<String, Object> tokenMap = new HashMap<>();
                                                    tokenMap.put("token_id", token_id);

                                                    mFirestore.collection("Users").document(current_id).update(tokenMap).addOnSuccessListener(new OnSuccessListener<Void>() {
                                                        @Override
                                                        public void onSuccess(Void aVoid) {

                                                            FirebaseFirestore.getInstance().collection("Users").document(current_id).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                                                                @Override
                                                                public void onSuccess(DocumentSnapshot documentSnapshot) {

                                                                    String username = documentSnapshot.getString("username");
                                                                    String name = documentSnapshot.getString("name");
                                                                    String email = documentSnapshot.getString("email");
                                                                    String image = documentSnapshot.getString("image");
                                                                    String password = pass_;
                                                                    String location = documentSnapshot.getString("location");
                                                                    String bio = documentSnapshot.getString("bio");

                                                                    // TODO Added Qiscus login here
                                                                    userHelper.insertContact(username, name, email, image, password, location, bio);
                                                                    loginPresenter.login(name, email, password);

                                                                    // TODO Add to Override option
                                                                    // Original placement of functions:
                                                                    mDialog.dismiss();
                                                                    MainActivity.startActivity(LoginActivity.this);
                                                                    finish();

                                                            }

...

Here's my code for FCM ...

public class FCMService extends FirebaseMessagingService {

    private static final String TAG = FCMService.class.getSimpleName();

    private NotificationUtil notificationUtils;
    private String cDesc;


    @Override
    public void onNewToken(String token) {
        super.onNewToken(token);
        Log.d("NEW_TOKEN",token);

        //String refreshedToken = FirebaseInstanceId.getInstance().getToken(); 
        // Deprecated
        storeRegIdInPref(token);
        sendRegistrationToServer(token);

        Intent registrationComplete = new Intent(Config.REGISTRATION_COMPLETE);
        registrationComplete.putExtra("token", token);
        LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
    }


    private void sendRegistrationToServer(final String token) {
        Log.i(TAG, "sendRegistrationToServer:" + token);
    }

    private void storeRegIdInPref(String token) {
        SharedPreferences pref = getApplicationContext().getSharedPreferences(Config.SHARED_PREF, MODE_PRIVATE);
        SharedPreferences.Editor editor = pref.edit();
        editor.putString("regId", token);
        editor.apply();
    }




    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        handleDataMessage(remoteMessage);
    }

   }

...

(Edit)Firestore Database Structure

I appreciate Frank's answer but here is the structure of my database. I understand that I may need to restructure my database so that it can hold multiple token_ids at once so that the friends/commenter cloud functions can do the work of deleting the tokens that produce error. But I quite frankly don't know how to do that. Any help or website welcomed.


回答1:


There is way too much code in your question, so I didn't read all of it. But the gist of the error message is that you're sending an instance ID token to FCM that is not (or no longer known). You will need to remove the token from your so-called token registry, usually simply the database where you keep the tokens.

My favorite example of how to remove expired tokens is in the functions-samples repo:

  tokens = Object.keys(tokensSnapshot.val());
  // Send notifications to all tokens.
  const response = await admin.messaging().sendToDevice(tokens, payload);
  // For each message check if there was an error.
  const tokensToRemove = [];
  response.results.forEach((result, index) => {
    const error = result.error;
    if (error) {
      console.error('Failure sending notification to', tokens[index], error);
      // Cleanup the tokens who are not registered anymore.
      if (error.code === 'messaging/invalid-registration-token' ||
          error.code === 'messaging/registration-token-not-registered') {
        tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
      }
    }
  });
  return Promise.all(tokensToRemove);

The above code calls sendToDevice(...), and then parses the results for expired token errors. It then deletes those tokens from the database, so that they won't keep causing errors.



来源:https://stackoverflow.com/questions/55545542/firebase-cloud-functions-tokens

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!