Skip to content

Ensure token refresh notification is triggered any time a new token is refreshed. b/77662386 #2562

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

Merged
merged 5 commits into from
Mar 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions Example/InstanceID/Tests/FIRInstanceIDTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ - (void)didCompleteConfigure;
- (NSString *)cachedTokenIfAvailable;
- (void)deleteIdentityWithHandler:(FIRInstanceIDDeleteHandler)handler;
+ (FIRInstanceID *)instanceIDForTests;
- (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler;
@end

@interface FIRInstanceIDTest : XCTestCase
Expand Down Expand Up @@ -157,7 +158,7 @@ - (void)testTokenShouldBeRefreshedIfCacheTokenNeedsToBeRefreshed {
handler:[OCMArg any]];

[self.mockInstanceID didCompleteConfigure];
OCMVerify([self.mockInstanceID fetchDefaultToken]);
OCMVerify([self.mockInstanceID defaultTokenWithHandler:nil]);
XCTAssertEqualObjects([self.mockInstanceID token], kToken);
}

Expand All @@ -172,7 +173,7 @@ - (void)testTokenShouldBeRefreshedIfNoCacheTokenButAutoInitAllowed {

[self.mockInstanceID didCompleteConfigure];

OCMVerify([self.mockInstanceID fetchDefaultToken]);
OCMVerify([self.mockInstanceID defaultTokenWithHandler:nil]);
}

- (void)testTokenIsDeletedAlongWithIdentity {
Expand Down Expand Up @@ -519,11 +520,11 @@ - (void)testDefaultToken_noCachedToken {
cachedTokenInfoWithAuthorizedEntity:kAuthorizedEntity
scope:@"*"];

OCMExpect([self.mockInstanceID fetchDefaultToken]);
OCMExpect([self.mockInstanceID defaultTokenWithHandler:nil]);
NSString *token = [self.mockInstanceID token];
XCTAssertNil(token);
[self.mockInstanceID stopMocking];
OCMVerify([self.mockInstanceID fetchDefaultToken]);
OCMVerify([self.mockInstanceID defaultTokenWithHandler:nil]);
}

/**
Expand All @@ -535,7 +536,7 @@ - (void)testDefaultToken_validCachedToken {
cachedTokenInfoWithAuthorizedEntity:kAuthorizedEntity
scope:@"*"];

[[self.mockInstanceID reject] fetchDefaultToken];
[[self.mockInstanceID reject] defaultTokenWithHandler:nil];
NSString *token = [self.mockInstanceID token];
XCTAssertEqualObjects(token, kToken);
}
Expand Down
6 changes: 0 additions & 6 deletions Firebase/InstanceID/FIRInstanceID+Testing.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@
*/
- (void)start;

/**
* Without checking any caches etc, always attempts to fetch the default token (unless a fetch
* is already in progress.
*/
- (void)fetchDefaultToken;

+ (int64_t)maxRetryCountForDefaultToken;
+ (int64_t)minIntervalForDefaultTokenRetry;
+ (int64_t)maxRetryIntervalForDefaultTokenInSeconds;
Expand Down
102 changes: 30 additions & 72 deletions Firebase/InstanceID/FIRInstanceID.m
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ - (NSString *)token {
// If we've never had a cached default token, we should fetch one because unrelatedly,
// this request will help us determine whether the locally-generated Instance ID keypair is not
// unique, and therefore generate a new one.
[self fetchDefaultToken];
[self defaultTokenWithHandler:nil];
return nil;
}
}
Expand All @@ -217,40 +217,16 @@ - (void)instanceIDWithHandler:(FIRInstanceIDResultHandler)handler {
// If no handler, simply return since client has generated iid and token.
return;
}

// Now get token
FIRInstanceIDTokenHandler tokenHandler = ^void(NSString *token, NSError *error) {
if (error) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007,
@"Failed to retrieve the default FCM token after %ld retries",
(long)self.retryCountForDefaultToken);
if (handler) {
// If token fetching fails, result should be nil with error returned.
[self defaultTokenWithHandler:^(NSString *_Nullable token, NSError *_Nullable error) {
if (handler) {
if (error) {
handler(nil, error);
return;
}
return;
}
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@",
token);
NSString *previousFCMToken = self.defaultFCMToken;
self.defaultFCMToken = token;

// Only notify of token refresh if we have a new valid token that's different than before
if (self.defaultFCMToken.length && ![self.defaultFCMToken isEqualToString:previousFCMToken]) {
NSNotification *tokenRefreshNotification =
[NSNotification notificationWithName:kFIRInstanceIDTokenRefreshNotification
object:[self.defaultFCMToken copy]];
[[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification
postingStyle:NSPostASAP];
}

if (handler) {
result.token = token;
handler(result, nil);
}
};

[self defaultTokenWithHandler:tokenHandler];
}];
}];
}

Expand Down Expand Up @@ -627,7 +603,7 @@ - (void)deleteIdentityWithHandler:(FIRInstanceIDDeleteHandler)handler {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(0.5 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[self fetchDefaultToken];
[self defaultTokenWithHandler:nil];
});
}
if (handler) {
Expand Down Expand Up @@ -727,7 +703,7 @@ - (void)didCompleteConfigure {
// Clean up expired tokens by checking the token refresh policy.
if ([self.tokenManager checkForTokenRefreshPolicy]) {
// Default token is expired, fetch default token from server.
[self fetchDefaultToken];
[self defaultTokenWithHandler:nil];
}
// Notify FCM with the default token.
#pragma clang diagnostic push
Expand All @@ -738,7 +714,7 @@ - (void)didCompleteConfigure {
// When there is no cached token, must check auto init is enabled.
// If it's disabled, don't initiate token generation/refresh.
// If no cache token and auto init is enabled, fetch a token from server.
[self fetchDefaultToken];
[self defaultTokenWithHandler:nil];
// Notify FCM with the default token.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Expand Down Expand Up @@ -855,40 +831,6 @@ - (NSInteger)retryIntervalToFetchDefaultToken {
kMaxRetryIntervalForDefaultTokenInSeconds);
}

- (void)fetchDefaultToken {
if (self.isFetchingDefaultToken) {
return;
}

FIRInstanceID_WEAKIFY(self);
FIRInstanceIDTokenHandler handler = ^void(NSString *token, NSError *error) {
FIRInstanceID_STRONGIFY(self);

if (error) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007,
@"Failed to retrieve the default FCM token after %ld retries",
(long)self.retryCountForDefaultToken);
} else {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@",
token);
NSString *previousFCMToken = self.defaultFCMToken;
self.defaultFCMToken = token;

// Only notify of token refresh if we have a new valid token that's different than before
if (self.defaultFCMToken.length && ![self.defaultFCMToken isEqualToString:previousFCMToken]) {
NSNotification *tokenRefreshNotification =
[NSNotification notificationWithName:kFIRInstanceIDTokenRefreshNotification
object:[self.defaultFCMToken copy]];
[[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification
postingStyle:NSPostASAP];
}
}
};

// Get a "*" token using this APNS token.
[self defaultTokenWithHandler:handler];
}

- (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler {
if (self.isFetchingDefaultToken || self.isDefaultTokenFetchScheduled) {
return;
Expand Down Expand Up @@ -938,14 +880,16 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler {
[self defaultTokenWithHandler:handler];
});
} else {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007,
@"Failed to retrieve the default FCM token after %ld retries",
(long)self.retryCountForDefaultToken);
if (handler) {
handler(nil, error);
}
}
} else {
// If somebody updated IID with APNS token while our initial request did not have it
// set we need to update it on the server.
BOOL shouldNotifyHandler = YES;
NSData *deviceTokenInRequest = instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSKey];
BOOL isSandboxInRequest =
[instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSIsSandboxKey] boolValue];
Expand All @@ -961,8 +905,6 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler {
// APNs value did change mid-fetch, so the token should be re-fetched with the current APNs
// value.
self.isDefaultTokenFetchScheduled = YES;
// Wait to notify until we can modify this token with APNS (or receive a new token)
shouldNotifyHandler = NO;
FIRInstanceID_WEAKIFY(self);
dispatch_async(dispatch_get_main_queue(), ^{
FIRInstanceID_STRONGIFY(self);
Expand All @@ -972,12 +914,28 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeRefetchingTokenForAPNS,
@"Received APNS token while fetching default token. "
@"Refetching default token.");
// Do not notify and handle completion handler since this is a retry.
// Simply return.
return;
} else {
FIRInstanceIDLoggerInfo(kFIRInstanceIDMessageCodeInstanceID010,
@"Successfully fetched default token.");
}
// Post the required notifications if somebody is waiting.
if (shouldNotifyHandler && handler) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@",
token);
NSString *previousFCMToken = self.defaultFCMToken;
self.defaultFCMToken = token;

// Only notify of token refresh if we have a new valid token that's different than before
if (self.defaultFCMToken.length && ![self.defaultFCMToken isEqualToString:previousFCMToken]) {
NSNotification *tokenRefreshNotification =
[NSNotification notificationWithName:kFIRInstanceIDTokenRefreshNotification
object:[self.defaultFCMToken copy]];
[[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification
postingStyle:NSPostASAP];
}
if (handler) {
handler(token, nil);
}
}
Expand Down Expand Up @@ -1040,7 +998,7 @@ - (void)notifyAPNSTokenIsSet:(NSNotification *)notification {
if ([tokenInfo.token isEqualToString:self.defaultFCMToken]) {
// We will perform a special fetch for the default FCM token, so that the delegate methods
// are called. For all others, we will do an internal re-fetch.
[self fetchDefaultToken];
[self defaultTokenWithHandler:nil];
} else {
[self.tokenManager fetchNewTokenWithAuthorizedEntity:tokenInfo.authorizedEntity
scope:tokenInfo.scope
Expand Down