Skip to content

FCM Tokens do not refresh 'deleteToken' -> 'getToken' is called #6433

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mtieltjes opened this issue Sep 10, 2020 · 10 comments
Closed

FCM Tokens do not refresh 'deleteToken' -> 'getToken' is called #6433

mtieltjes opened this issue Sep 10, 2020 · 10 comments
Assignees

Comments

@mtieltjes
Copy link

mtieltjes commented Sep 10, 2020

Step 1: Describe your environment

  • Xcode version: 11.6
  • Firebase SDK version: 6.30.0
  • Firebase Component: Messaging
  • Component version: 4.6.2
  • Installation method: CocoaPods (through React Native Firebase)

Step 2: Describe the problem

When deleting the FCM token, and getting a new one, the same (cached & invalid) token is returned.
By stepping through breakpoints I found the lines that cause this behavior; see relevant code

Steps to reproduce:

In our ReactNative project, we use React Native Firebase as bridge to the Firebase SDK.
I copied the relevant code of that library, of which our usage boils down to this:

Objective-C code

Link to original code

RCT_EXPORT_METHOD(getToken:
  (NSString *) authorizedEntity
    :(NSString *) scope
    :(RCTPromiseResolveBlock) resolve
    :(RCTPromiseRejectBlock) reject
) {
  if ([scope isEqualToString:@"FCM"] && [authorizedEntity isEqualToString:[FIRApp defaultApp].options.GCMSenderID]) {
    [[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult *_Nullable result, NSError *_Nullable error) {
      if (error) {
        [RNFBSharedUtils rejectPromiseWithNSError:reject error:error];
      } else {
        resolve(result.token);
      }
    }];
  } else {
    NSDictionary *options = nil;
    if ([FIRMessaging messaging].APNSToken) {
      options = @{@"apns_token": [FIRMessaging messaging].APNSToken};
    }

    [[FIRInstanceID instanceID] tokenWithAuthorizedEntity:authorizedEntity scope:scope options:options handler:^(NSString *_Nullable identity, NSError *_Nullable error) {
      if (error) {
        [RNFBSharedUtils rejectPromiseWithNSError:reject error:error];
      } else {
        resolve(identity);
      }
    }];
  }
}

RCT_EXPORT_METHOD(deleteToken:
  (NSString *) authorizedEntity
    :(NSString *) scope
    :(RCTPromiseResolveBlock) resolve
    :(RCTPromiseRejectBlock) reject
) {
  [[FIRInstanceID instanceID] deleteTokenWithAuthorizedEntity:authorizedEntity scope:scope handler:^(NSError *_Nullable error) {
    if (error) {
      [RNFBSharedUtils rejectPromiseWithNSError:reject error:error];
    } else {
      resolve([NSNull null]);
    }
  }];
}
Which is then being called from the React Native part of the library

Link to original code

  getToken(authorizedEntity, scope) {
    return this.native.getToken(
      authorizedEntity || this.app.options.messagingSenderId, // GCMSenderId
      scope || 'FCM',
    );
  }

  deleteToken(authorizedEntity, scope) {
    return this.native.deleteToken(
      authorizedEntity || this.app.options.messagingSenderId, // GCMSenderId
      scope || 'FCM',
    );
  }

All we do with that library is call getToken() and deleteToken() without parameters, using the defaults as declared above.
The result of this is:

await getToken(); // cvmV.....UCiW1
await deleteToken(); 

// Expecting a new token now, since the above one is invalid
await getToken(); // cvmV.....UCiW1

Relevant Code:

This line does not get called, where I would expect it to. The surrounding if results in false

[self.instanceIDStore removeCachedTokenWithAuthorizedEntity:authorizedEntity scope:scope];

Then, when getting the new token, the cached version is returned here:

@charlotteliang
Copy link
Contributor

To first understand your use case, won't the function fail if you pass in nil as authorizedEntity?

Can you share your sample code of calling the public API?

@mtieltjes
Copy link
Author

mtieltjes commented Sep 16, 2020

Sorry, I was a bit in a hurry when writing the issue.

won't the function fail if you pass in nil as authorizedEntity?

You're right about that. I got a little confused as we call our library without any parameters, but the authorizedEntity defaults to the FIRApp options GCMSenderID

Our project is created in React Native, and we use React Native Firebase as a bridge to the Firebase SDK.
To prevent filing an issue at the wrong project, I did a little debugging in both libraries and it appears to me that RNFirebase is calling the API correctly, but chances are I overlooked something.

I added all the related code with links to the original files in the OP, hopefully this clears up our use case a bit.

I'll try to get a React Native example project with the above bug reproducible.

@golya
Copy link

golya commented Sep 16, 2020

@mtieltjes, @chliangGoogle We have a similar issue, and if you check you can see that in the cache there is a key senderid.*, and when the RNF try to delete the token by the firebase-ios-sdk then the default scope is "FCM", so it try to find the senderid.FCM key.
161 scope || 'FCM',

If you use this code in javascript, then it can be quick workaround for you (I tested it and we got a new token):
await messaging().deleteToken(undefined, '*');

So I am not sure that the bug is inside the firebase-ios-sdk.

On the other hand I am not sure that what happens when the FCM invalidating a token, dose that event triggering with the right scope? Or the firebase-ios-sdk has the bug that not save the token inside the cache with senderid.FCM?

@charlotteliang
Copy link
Contributor

@golya We do save the token per sender ID per scope, and when you invalidate the token by calling deleteToken, we will clear the cache and send a request to our server to invalidate it too.
Does that answer your question?

@golya
Copy link

golya commented Sep 17, 2020

@chliangGoogle Thanks for your answer, based on my debug it seems the scope is always a constant and a hardcoded string during the save and when you try to find it in the cache by the instance handler:
https://github.com/firebase/firebase-ios-sdk/blob/release-6.33.0/Firebase/InstanceID/FIRInstanceIDConstants.m#L33
https://github.com/firebase/firebase-ios-sdk/blob/release-6.33.0/Firebase/InstanceID/FIRInstanceIDTokenManager.m#L345
https://github.com/firebase/firebase-ios-sdk/blob/master/Firebase/InstanceID/FIRInstanceID.m#L239

Probably I do not see the whole picture and operations, but it seems using the instance handler you can not really control the scope. Where can I find that code part where you save the token per senderID per scope?

@charlotteliang
Copy link
Contributor

@golya That's for storing the default token which is the most common case.
We support store token per senderID and per scope if you call the tokenWithAuthorizedEntity: method.
https://github.com/firebase/firebase-ios-sdk/blob/release-6.33.0/Firebase/InstanceID/FIRInstanceIDTokenManager.m#L104

@golya
Copy link

golya commented Sep 18, 2020

@chliangGoogle Got it, thank you for the quick response! :)

In this case my question if someone would like to use the instanceIDWithHandler for getting back the token then it is impossible to use the scope, am I right?
https://github.com/firebase/firebase-ios-sdk/blob/Messaging-4.6.2/Firebase/InstanceID/FIRInstanceID.m#L201

@golya
Copy link

golya commented Oct 2, 2020

@chliangGoogle can you confirm please that the sdk can handle scope related tokens in the cache? :)

@charlotteliang
Copy link
Contributor

Yes, as explained in thread above.

And if someone use instanceIDWithHandler, then no, because it's using the default scope.

@golya
Copy link

golya commented Oct 5, 2020

@chliangGoogle thanks!! In this case I am not sure that this issue is make sense at all. I suppose that we can close it.

@firebase firebase locked and limited conversation to collaborators Nov 5, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants