FIRInstanceID.m 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186
  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FIRInstanceID.h"
  17. #import <FirebaseCore/FIRAppInternal.h>
  18. #import <FirebaseCore/FIRComponent.h>
  19. #import <FirebaseCore/FIRComponentContainer.h>
  20. #import <FirebaseCore/FIRLibrary.h>
  21. #import <FirebaseCore/FIROptions.h>
  22. #import <GoogleUtilities/GULAppEnvironmentUtil.h>
  23. #import "FIRInstanceID+Private.h"
  24. #import "FIRInstanceIDAuthService.h"
  25. #import "FIRInstanceIDCombinedHandler.h"
  26. #import "FIRInstanceIDConstants.h"
  27. #import "FIRInstanceIDDefines.h"
  28. #import "FIRInstanceIDKeyPairStore.h"
  29. #import "FIRInstanceIDLogger.h"
  30. #import "FIRInstanceIDStore.h"
  31. #import "FIRInstanceIDTokenInfo.h"
  32. #import "FIRInstanceIDTokenManager.h"
  33. #import "FIRInstanceIDUtilities.h"
  34. #import "FIRInstanceIDVersionUtilities.h"
  35. #import "NSError+FIRInstanceID.h"
  36. // Public constants
  37. NSString *const kFIRInstanceIDScopeFirebaseMessaging = @"fcm";
  38. #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  39. const NSNotificationName kFIRInstanceIDTokenRefreshNotification =
  40. @"com.firebase.iid.notif.refresh-token";
  41. #else
  42. NSString *const kFIRInstanceIDTokenRefreshNotification = @"com.firebase.iid.notif.refresh-token";
  43. #endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  44. NSString *const kFIRInstanceIDInvalidNilHandlerError = @"Invalid nil handler.";
  45. // Private constants
  46. int64_t const kMaxRetryIntervalForDefaultTokenInSeconds = 20 * 60; // 20 minutes
  47. int64_t const kMinRetryIntervalForDefaultTokenInSeconds = 10; // 10 seconds
  48. // we retry only a max 5 times.
  49. // TODO(chliangGoogle): If we still fail we should listen for the network change notification
  50. // since GCM would have started Reachability. We only start retrying after we see a configuration
  51. // change.
  52. NSInteger const kMaxRetryCountForDefaultToken = 5;
  53. static NSString *const kEntitlementsAPSEnvironmentKey = @"Entitlements.aps-environment";
  54. static NSString *const kAPSEnvironmentDevelopmentValue = @"development";
  55. /// FIRMessaging selector that returns the current FIRMessaging auto init
  56. /// enabled flag.
  57. static NSString *const kFIRInstanceIDFCMSelectorAutoInitEnabled = @"isAutoInitEnabled";
  58. static NSString *const kFIRInstanceIDFCMSelectorInstance = @"messaging";
  59. static NSString *const kFIRInstanceIDAPNSTokenType = @"APNSTokenType";
  60. static NSString *const kFIRIIDAppReadyToConfigureSDKNotification =
  61. @"FIRAppReadyToConfigureSDKNotification";
  62. static NSString *const kFIRIIDAppNameKey = @"FIRAppNameKey";
  63. static NSString *const kFIRIIDErrorDomain = @"com.firebase.instanceid";
  64. static NSString *const kFIRIIDServiceInstanceID = @"InstanceID";
  65. static NSInteger const kFIRIIDErrorCodeInstanceIDFailed = -121;
  66. typedef void (^FIRInstanceIDKeyPairHandler)(FIRInstanceIDKeyPair *keyPair, NSError *error);
  67. /**
  68. * The APNS token type for the app. If the token type is set to `UNKNOWN`
  69. * InstanceID will implicitly try to figure out what the actual token type
  70. * is from the provisioning profile.
  71. * This must match FIRMessagingAPNSTokenType in FIRMessaging.h
  72. */
  73. typedef NS_ENUM(NSInteger, FIRInstanceIDAPNSTokenType) {
  74. /// Unknown token type.
  75. FIRInstanceIDAPNSTokenTypeUnknown,
  76. /// Sandbox token type.
  77. FIRInstanceIDAPNSTokenTypeSandbox,
  78. /// Production token type.
  79. FIRInstanceIDAPNSTokenTypeProd,
  80. } NS_SWIFT_NAME(InstanceIDAPNSTokenType);
  81. @interface FIRInstanceIDResult ()
  82. @property(nonatomic, readwrite, copy) NSString *instanceID;
  83. @property(nonatomic, readwrite, copy) NSString *token;
  84. @end
  85. @interface FIRInstanceID ()
  86. // FIRApp configuration objects.
  87. @property(nonatomic, readwrite, copy) NSString *fcmSenderID;
  88. @property(nonatomic, readwrite, copy) NSString *firebaseAppID;
  89. // Raw APNS token data
  90. @property(nonatomic, readwrite, strong) NSData *apnsTokenData;
  91. @property(nonatomic, readwrite) FIRInstanceIDAPNSTokenType apnsTokenType;
  92. // String-based, internal representation of APNS token
  93. @property(nonatomic, readwrite, copy) NSString *APNSTupleString;
  94. // Token fetched from the server automatically for the default app.
  95. @property(nonatomic, readwrite, copy) NSString *defaultFCMToken;
  96. @property(nonatomic, readwrite, strong) FIRInstanceIDTokenManager *tokenManager;
  97. @property(nonatomic, readwrite, strong) FIRInstanceIDKeyPairStore *keyPairStore;
  98. // backoff and retry for default token
  99. @property(nonatomic, readwrite, assign) NSInteger retryCountForDefaultToken;
  100. @property(atomic, strong, nullable)
  101. FIRInstanceIDCombinedHandler<NSString *> *defaultTokenFetchHandler;
  102. @end
  103. // InstanceID doesn't provide any functionality to other components,
  104. // so it provides a private, empty protocol that it conforms to and use it for registration.
  105. @protocol FIRInstanceIDInstanceProvider
  106. @end
  107. @interface FIRInstanceID () <FIRInstanceIDInstanceProvider, FIRLibrary>
  108. @end
  109. @implementation FIRInstanceIDResult
  110. - (id)copyWithZone:(NSZone *)zone {
  111. FIRInstanceIDResult *result = [[[self class] allocWithZone:zone] init];
  112. result.instanceID = self.instanceID;
  113. result.token = self.token;
  114. return result;
  115. }
  116. @end
  117. @implementation FIRInstanceID
  118. // File static to support InstanceID tests that call [FIRInstanceID instanceID] after
  119. // [FIRInstanceID instanceIDForTests].
  120. static FIRInstanceID *gInstanceID;
  121. + (instancetype)instanceID {
  122. // If the static instance was created, return it. This should only be set in tests and we should
  123. // eventually use proper dependency injection for a better test structure.
  124. if (gInstanceID != nil) {
  125. return gInstanceID;
  126. }
  127. FIRApp *defaultApp = [FIRApp defaultApp]; // Missing configure will be logged here.
  128. FIRInstanceID *instanceID =
  129. (FIRInstanceID *)FIR_COMPONENT(FIRInstanceIDInstanceProvider, defaultApp.container);
  130. return instanceID;
  131. }
  132. - (instancetype)initPrivately {
  133. self = [super init];
  134. if (self != nil) {
  135. // Use automatic detection of sandbox, unless otherwise set by developer
  136. _apnsTokenType = FIRInstanceIDAPNSTokenTypeUnknown;
  137. }
  138. return self;
  139. }
  140. + (FIRInstanceID *)instanceIDForTests {
  141. gInstanceID = [[FIRInstanceID alloc] initPrivately];
  142. [gInstanceID start];
  143. return gInstanceID;
  144. }
  145. - (void)dealloc {
  146. [[NSNotificationCenter defaultCenter] removeObserver:self];
  147. }
  148. #pragma mark - Tokens
  149. - (NSString *)token {
  150. if (!self.fcmSenderID.length) {
  151. return nil;
  152. }
  153. NSString *cachedToken = [self cachedTokenIfAvailable];
  154. if (cachedToken) {
  155. return cachedToken;
  156. } else {
  157. // If we've never had a cached default token, we should fetch one because unrelatedly,
  158. // this request will help us determine whether the locally-generated Instance ID keypair is not
  159. // unique, and therefore generate a new one.
  160. [self defaultTokenWithHandler:nil];
  161. return nil;
  162. }
  163. }
  164. - (void)instanceIDWithHandler:(FIRInstanceIDResultHandler)handler {
  165. FIRInstanceID_WEAKIFY(self);
  166. [self getIDWithHandler:^(NSString *identity, NSError *error) {
  167. FIRInstanceID_STRONGIFY(self);
  168. // This is in main queue already
  169. if (error) {
  170. if (handler) {
  171. handler(nil, error);
  172. }
  173. return;
  174. }
  175. FIRInstanceIDResult *result = [[FIRInstanceIDResult alloc] init];
  176. result.instanceID = identity;
  177. NSString *cachedToken = [self cachedTokenIfAvailable];
  178. if (cachedToken) {
  179. if (handler) {
  180. result.token = cachedToken;
  181. handler(result, nil);
  182. }
  183. // If no handler, simply return since client has generated iid and token.
  184. return;
  185. }
  186. [self defaultTokenWithHandler:^(NSString *_Nullable token, NSError *_Nullable error) {
  187. if (handler) {
  188. if (error) {
  189. handler(nil, error);
  190. return;
  191. }
  192. result.token = token;
  193. handler(result, nil);
  194. }
  195. }];
  196. }];
  197. }
  198. - (NSString *)cachedTokenIfAvailable {
  199. FIRInstanceIDTokenInfo *cachedTokenInfo =
  200. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:self.fcmSenderID
  201. scope:kFIRInstanceIDDefaultTokenScope];
  202. return cachedTokenInfo.token;
  203. }
  204. - (void)setDefaultFCMToken:(NSString *)defaultFCMToken {
  205. if (_defaultFCMToken && defaultFCMToken && [defaultFCMToken isEqualToString:_defaultFCMToken]) {
  206. return;
  207. }
  208. _defaultFCMToken = defaultFCMToken;
  209. // Sending this notification out will ensure that FIRMessaging has the updated
  210. // default FCM token.
  211. NSNotification *internalDefaultTokenNotification =
  212. [NSNotification notificationWithName:kFIRInstanceIDDefaultGCMTokenNotification
  213. object:_defaultFCMToken];
  214. [[NSNotificationQueue defaultQueue] enqueueNotification:internalDefaultTokenNotification
  215. postingStyle:NSPostASAP];
  216. }
  217. - (void)tokenWithAuthorizedEntity:(NSString *)authorizedEntity
  218. scope:(NSString *)scope
  219. options:(NSDictionary *)options
  220. handler:(FIRInstanceIDTokenHandler)handler {
  221. _FIRInstanceIDDevAssert(handler != nil && [authorizedEntity length] && [scope length],
  222. @"Invalid authorizedEntity or scope to new token");
  223. if (!handler) {
  224. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID000,
  225. kFIRInstanceIDInvalidNilHandlerError);
  226. return;
  227. }
  228. NSMutableDictionary *tokenOptions = [NSMutableDictionary dictionary];
  229. if (options.count) {
  230. [tokenOptions addEntriesFromDictionary:options];
  231. }
  232. NSString *APNSKey = kFIRInstanceIDTokenOptionsAPNSKey;
  233. NSString *serverTypeKey = kFIRInstanceIDTokenOptionsAPNSIsSandboxKey;
  234. if (tokenOptions[APNSKey] != nil && tokenOptions[serverTypeKey] == nil) {
  235. // APNS key was given, but server type is missing. Supply the server type with automatic
  236. // checking. This can happen when the token is requested from FCM, which does not include a
  237. // server type during its request.
  238. tokenOptions[serverTypeKey] = @([self isSandboxApp]);
  239. }
  240. // comparing enums to ints directly throws a warning
  241. FIRInstanceIDErrorCode noError = INT_MAX;
  242. FIRInstanceIDErrorCode errorCode = noError;
  243. if (FIRInstanceIDIsValidGCMScope(scope) && !tokenOptions[APNSKey]) {
  244. errorCode = kFIRInstanceIDErrorCodeMissingAPNSToken;
  245. } else if (FIRInstanceIDIsValidGCMScope(scope) &&
  246. ![tokenOptions[APNSKey] isKindOfClass:[NSData class]]) {
  247. errorCode = kFIRInstanceIDErrorCodeInvalidRequest;
  248. } else if (![authorizedEntity length]) {
  249. errorCode = kFIRInstanceIDErrorCodeInvalidAuthorizedEntity;
  250. } else if (![scope length]) {
  251. errorCode = kFIRInstanceIDErrorCodeInvalidScope;
  252. } else if (!self.keyPairStore) {
  253. errorCode = kFIRInstanceIDErrorCodeInvalidStart;
  254. }
  255. FIRInstanceIDTokenHandler newHandler = ^(NSString *token, NSError *error) {
  256. dispatch_async(dispatch_get_main_queue(), ^{
  257. handler(token, error);
  258. });
  259. };
  260. if (errorCode != noError) {
  261. newHandler(nil, [NSError errorWithFIRInstanceIDErrorCode:errorCode]);
  262. return;
  263. }
  264. // TODO(chliangGoogle): Add some validation logic that the APNs token data and sandbox value are
  265. // supplied in the valid format (NSData and BOOL, respectively).
  266. // Add internal options
  267. if (self.firebaseAppID) {
  268. tokenOptions[kFIRInstanceIDTokenOptionsFirebaseAppIDKey] = self.firebaseAppID;
  269. }
  270. FIRInstanceID_WEAKIFY(self);
  271. FIRInstanceIDAuthService *authService = self.tokenManager.authService;
  272. [authService
  273. fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) {
  274. FIRInstanceID_STRONGIFY(self);
  275. if (error) {
  276. newHandler(nil, error);
  277. return;
  278. }
  279. // Only use the token in the cache if the APNSInfo matches what the request's options has.
  280. // It's possible for the request to be with a newer APNs device token, which should be
  281. // honored.
  282. FIRInstanceIDTokenInfo *cachedTokenInfo =
  283. [self.tokenManager cachedTokenInfoWithAuthorizedEntity:authorizedEntity scope:scope];
  284. if (cachedTokenInfo) {
  285. // Ensure that the cached token matches APNs data before returning it.
  286. FIRInstanceIDAPNSInfo *optionsAPNSInfo =
  287. [[FIRInstanceIDAPNSInfo alloc] initWithTokenOptionsDictionary:tokenOptions];
  288. // If either the APNs info is missing in both, or if they are an exact match, then we can
  289. // use this cached token.
  290. if ((!cachedTokenInfo.APNSInfo && !optionsAPNSInfo) ||
  291. [cachedTokenInfo.APNSInfo isEqualToAPNSInfo:optionsAPNSInfo]) {
  292. newHandler(cachedTokenInfo.token, nil);
  293. return;
  294. }
  295. }
  296. FIRInstanceID_WEAKIFY(self);
  297. [self asyncLoadKeyPairWithHandler:^(FIRInstanceIDKeyPair *keyPair, NSError *error) {
  298. FIRInstanceID_STRONGIFY(self);
  299. if (error) {
  300. NSError *newError =
  301. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair];
  302. newHandler(nil, newError);
  303. } else {
  304. [self.tokenManager fetchNewTokenWithAuthorizedEntity:[authorizedEntity copy]
  305. scope:[scope copy]
  306. keyPair:keyPair
  307. options:tokenOptions
  308. handler:newHandler];
  309. }
  310. }];
  311. }];
  312. }
  313. - (void)deleteTokenWithAuthorizedEntity:(NSString *)authorizedEntity
  314. scope:(NSString *)scope
  315. handler:(FIRInstanceIDDeleteTokenHandler)handler {
  316. _FIRInstanceIDDevAssert(handler != nil && [authorizedEntity length] && [scope length],
  317. @"Invalid authorizedEntity or scope to delete token");
  318. if (!handler) {
  319. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID001,
  320. kFIRInstanceIDInvalidNilHandlerError);
  321. }
  322. // comparing enums to ints directly throws a warning
  323. FIRInstanceIDErrorCode noError = INT_MAX;
  324. FIRInstanceIDErrorCode errorCode = noError;
  325. if (![authorizedEntity length]) {
  326. errorCode = kFIRInstanceIDErrorCodeInvalidAuthorizedEntity;
  327. } else if (![scope length]) {
  328. errorCode = kFIRInstanceIDErrorCodeInvalidScope;
  329. } else if (!self.keyPairStore) {
  330. errorCode = kFIRInstanceIDErrorCodeInvalidStart;
  331. }
  332. FIRInstanceIDDeleteTokenHandler newHandler = ^(NSError *error) {
  333. // If a default token is deleted successfully, reset the defaultFCMToken too.
  334. if (!error && [authorizedEntity isEqualToString:self.fcmSenderID] &&
  335. [scope isEqualToString:kFIRInstanceIDDefaultTokenScope]) {
  336. self.defaultFCMToken = nil;
  337. }
  338. dispatch_async(dispatch_get_main_queue(), ^{
  339. handler(error);
  340. });
  341. };
  342. if (errorCode != noError) {
  343. newHandler([NSError errorWithFIRInstanceIDErrorCode:errorCode]);
  344. return;
  345. }
  346. FIRInstanceID_WEAKIFY(self);
  347. FIRInstanceIDAuthService *authService = self.tokenManager.authService;
  348. [authService
  349. fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) {
  350. FIRInstanceID_STRONGIFY(self);
  351. if (error) {
  352. newHandler(error);
  353. return;
  354. }
  355. FIRInstanceID_WEAKIFY(self);
  356. [self asyncLoadKeyPairWithHandler:^(FIRInstanceIDKeyPair *keyPair, NSError *error) {
  357. FIRInstanceID_STRONGIFY(self);
  358. if (error) {
  359. NSError *newError =
  360. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair];
  361. newHandler(newError);
  362. } else {
  363. [self.tokenManager deleteTokenWithAuthorizedEntity:authorizedEntity
  364. scope:scope
  365. keyPair:keyPair
  366. handler:newHandler];
  367. }
  368. }];
  369. }];
  370. }
  371. - (void)asyncLoadKeyPairWithHandler:(FIRInstanceIDKeyPairHandler)handler {
  372. FIRInstanceID_WEAKIFY(self);
  373. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  374. FIRInstanceID_STRONGIFY(self);
  375. NSError *error = nil;
  376. FIRInstanceIDKeyPair *keyPair = [self.keyPairStore loadKeyPairWithError:&error];
  377. dispatch_async(dispatch_get_main_queue(), ^{
  378. if (error) {
  379. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID002,
  380. @"Failed to retreieve keyPair %@", error);
  381. if (handler) {
  382. handler(nil, error);
  383. }
  384. } else if (!keyPair && !error) {
  385. if (handler) {
  386. handler(nil,
  387. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair]);
  388. }
  389. } else {
  390. if (handler) {
  391. handler(keyPair, nil);
  392. }
  393. }
  394. });
  395. });
  396. }
  397. #pragma mark - Identity
  398. - (void)getIDWithHandler:(FIRInstanceIDHandler)handler {
  399. _FIRInstanceIDDevAssert(handler, @"Invalid nil handler to getIdentity");
  400. if (!handler) {
  401. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID003,
  402. kFIRInstanceIDInvalidNilHandlerError);
  403. return;
  404. }
  405. void (^callHandlerOnMainThread)(NSString *, NSError *) = ^(NSString *identity, NSError *error) {
  406. dispatch_async(dispatch_get_main_queue(), ^{
  407. handler(identity, error);
  408. });
  409. };
  410. if (!self.keyPairStore) {
  411. NSError *error = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidStart];
  412. callHandlerOnMainThread(nil, error);
  413. return;
  414. }
  415. FIRInstanceID_WEAKIFY(self);
  416. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  417. FIRInstanceID_STRONGIFY(self);
  418. NSError *error;
  419. NSString *appIdentity = [self.keyPairStore appIdentityWithError:&error];
  420. // When getID is explicitly called, trigger getToken to make sure token always exists.
  421. // This is to avoid ID conflict (ID is not checked for conflict until we generate a token)
  422. if (appIdentity) {
  423. [self token];
  424. }
  425. callHandlerOnMainThread(appIdentity, error);
  426. });
  427. }
  428. - (void)deleteIDWithHandler:(FIRInstanceIDDeleteHandler)handler {
  429. _FIRInstanceIDDevAssert(handler, @"Invalid nil handler to delete Identity");
  430. if (!handler) {
  431. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID004,
  432. kFIRInstanceIDInvalidNilHandlerError);
  433. return;
  434. }
  435. void (^callHandlerOnMainThread)(NSError *) = ^(NSError *error) {
  436. if ([NSThread isMainThread]) {
  437. handler(error);
  438. return;
  439. }
  440. dispatch_async(dispatch_get_main_queue(), ^{
  441. handler(error);
  442. });
  443. };
  444. if (!self.keyPairStore) {
  445. FIRInstanceIDErrorCode error = kFIRInstanceIDErrorCodeInvalidStart;
  446. callHandlerOnMainThread([NSError errorWithFIRInstanceIDErrorCode:error]);
  447. return;
  448. }
  449. FIRInstanceID_WEAKIFY(self);
  450. void (^deleteTokensHandler)(NSError *) = ^void(NSError *error) {
  451. FIRInstanceID_STRONGIFY(self);
  452. if (error) {
  453. callHandlerOnMainThread(error);
  454. return;
  455. }
  456. [self deleteIdentityWithHandler:^(NSError *error) {
  457. callHandlerOnMainThread(error);
  458. }];
  459. };
  460. [self asyncLoadKeyPairWithHandler:^(FIRInstanceIDKeyPair *keyPair, NSError *error) {
  461. FIRInstanceID_STRONGIFY(self);
  462. if (error) {
  463. NSError *newError =
  464. [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPair];
  465. callHandlerOnMainThread(newError);
  466. } else {
  467. [self.tokenManager deleteAllTokensWithKeyPair:keyPair handler:deleteTokensHandler];
  468. }
  469. }];
  470. }
  471. - (void)notifyIdentityReset {
  472. [self deleteIdentityWithHandler:nil];
  473. }
  474. // Delete all the local cache checkin, IID and token.
  475. - (void)deleteIdentityWithHandler:(FIRInstanceIDDeleteHandler)handler {
  476. // Delete tokens.
  477. [self.tokenManager deleteAllTokensLocallyWithHandler:^(NSError *deleteTokenError) {
  478. // Reset FCM token.
  479. self.defaultFCMToken = nil;
  480. if (deleteTokenError) {
  481. if (handler) {
  482. handler(deleteTokenError);
  483. }
  484. return;
  485. }
  486. // Delete Instance ID.
  487. [self.keyPairStore
  488. deleteSavedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType
  489. handler:^(NSError *error) {
  490. NSError *deletePlistError;
  491. [self.keyPairStore
  492. removeKeyPairCreationTimePlistWithError:&deletePlistError];
  493. if (error || deletePlistError) {
  494. if (handler) {
  495. // Prefer to use the delete Instance ID error.
  496. error = [NSError
  497. errorWithFIRInstanceIDErrorCode:
  498. kFIRInstanceIDErrorCodeUnknown
  499. userInfo:@{
  500. NSUnderlyingErrorKey : error
  501. ? error
  502. : deletePlistError
  503. }];
  504. handler(error);
  505. }
  506. return;
  507. }
  508. // Delete checkin.
  509. [self.tokenManager.authService
  510. resetCheckinWithHandler:^(NSError *error) {
  511. if (error) {
  512. if (handler) {
  513. handler(error);
  514. }
  515. return;
  516. }
  517. // Only request new token if FCM auto initialization is
  518. // enabled.
  519. if ([self isFCMAutoInitEnabled]) {
  520. // Deletion succeeds! Requesting new checkin, IID and token.
  521. // TODO(chliangGoogle) see if dispatch_after is necessary
  522. dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
  523. (int64_t)(0.5 * NSEC_PER_SEC)),
  524. dispatch_get_main_queue(), ^{
  525. [self defaultTokenWithHandler:nil];
  526. });
  527. }
  528. if (handler) {
  529. handler(nil);
  530. }
  531. }];
  532. }];
  533. }];
  534. }
  535. #pragma mark - Config
  536. + (void)load {
  537. [FIRApp registerInternalLibrary:(Class<FIRLibrary>)self
  538. withName:@"fire-iid"
  539. withVersion:FIRInstanceIDCurrentLibraryVersion()];
  540. }
  541. + (nonnull NSArray<FIRComponent *> *)componentsToRegister {
  542. FIRComponentCreationBlock creationBlock =
  543. ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
  544. // Ensure it's cached so it returns the same instance every time instanceID is called.
  545. *isCacheable = YES;
  546. FIRInstanceID *instanceID = [[FIRInstanceID alloc] initPrivately];
  547. [instanceID start];
  548. return instanceID;
  549. };
  550. FIRComponent *instanceIDProvider =
  551. [FIRComponent componentWithProtocol:@protocol(FIRInstanceIDInstanceProvider)
  552. instantiationTiming:FIRInstantiationTimingLazy
  553. dependencies:@[]
  554. creationBlock:creationBlock];
  555. return @[ instanceIDProvider ];
  556. }
  557. + (void)configureWithApp:(FIRApp *)app {
  558. if (!app.isDefaultApp) {
  559. // Only configure for the default FIRApp.
  560. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeFIRApp002,
  561. @"Firebase Instance ID only works with the default app.");
  562. return;
  563. }
  564. [[FIRInstanceID instanceID] configureInstanceIDWithOptions:app.options app:app];
  565. }
  566. - (void)configureInstanceIDWithOptions:(FIROptions *)options app:(FIRApp *)firApp {
  567. NSString *GCMSenderID = options.GCMSenderID;
  568. if (!GCMSenderID.length) {
  569. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeFIRApp000,
  570. @"Firebase not set up correctly, nil or empty senderID.");
  571. [FIRInstanceID exitWithReason:@"GCM_SENDER_ID must not be nil or empty." forFirebaseApp:firApp];
  572. return;
  573. }
  574. self.fcmSenderID = GCMSenderID;
  575. self.firebaseAppID = firApp.options.googleAppID;
  576. // FCM generates a FCM token during app start for sending push notification to device.
  577. // This is not needed for app extension.
  578. if (![GULAppEnvironmentUtil isAppExtension]) {
  579. [self didCompleteConfigure];
  580. }
  581. }
  582. + (NSError *)configureErrorWithReason:(nonnull NSString *)reason {
  583. NSString *description =
  584. [NSString stringWithFormat:@"Configuration failed for service %@.", kFIRIIDServiceInstanceID];
  585. if (!reason.length) {
  586. reason = @"Unknown reason";
  587. }
  588. NSDictionary *userInfo =
  589. @{NSLocalizedDescriptionKey : description, NSLocalizedFailureReasonErrorKey : reason};
  590. return [NSError errorWithDomain:kFIRIIDErrorDomain
  591. code:kFIRIIDErrorCodeInstanceIDFailed
  592. userInfo:userInfo];
  593. }
  594. // If the firebaseApp is available we should send logs for the error through it before
  595. // raising an exception.
  596. + (void)exitWithReason:(nonnull NSString *)reason forFirebaseApp:(FIRApp *)firebaseApp {
  597. [firebaseApp sendLogsWithServiceName:kFIRIIDServiceInstanceID
  598. version:FIRInstanceIDCurrentLibraryVersion()
  599. error:[self configureErrorWithReason:reason]];
  600. [NSException raise:kFIRIIDErrorDomain
  601. format:@"Could not configure Firebase InstanceID. %@", reason];
  602. }
  603. // This is used to start any operations when we receive FirebaseSDK setup notification
  604. // from FIRCore.
  605. - (void)didCompleteConfigure {
  606. NSString *cachedToken = [self cachedTokenIfAvailable];
  607. // When there is a cached token, do the token refresh.
  608. if (cachedToken) {
  609. // Clean up expired tokens by checking the token refresh policy.
  610. if ([self.tokenManager checkForTokenRefreshPolicy]) {
  611. // Default token is expired, fetch default token from server.
  612. [self defaultTokenWithHandler:nil];
  613. }
  614. // Notify FCM with the default token.
  615. self.defaultFCMToken = [self token];
  616. } else if ([self isFCMAutoInitEnabled]) {
  617. // When there is no cached token, must check auto init is enabled.
  618. // If it's disabled, don't initiate token generation/refresh.
  619. // If no cache token and auto init is enabled, fetch a token from server.
  620. [self defaultTokenWithHandler:nil];
  621. // Notify FCM with the default token.
  622. self.defaultFCMToken = [self token];
  623. }
  624. // ONLY checkin when auto data collection is turned on.
  625. if ([self isFCMAutoInitEnabled]) {
  626. [self.tokenManager.authService scheduleCheckin:YES];
  627. }
  628. }
  629. - (BOOL)isFCMAutoInitEnabled {
  630. Class messagingClass = NSClassFromString(kFIRInstanceIDFCMSDKClassString);
  631. // Firebase Messaging is not installed, auto init should be disabled since it's for FCM.
  632. if (!messagingClass) {
  633. return NO;
  634. }
  635. // Messaging doesn't have the singleton method, auto init should be enabled since FCM exists.
  636. SEL instanceSelector = NSSelectorFromString(kFIRInstanceIDFCMSelectorInstance);
  637. if (![messagingClass respondsToSelector:instanceSelector]) {
  638. return YES;
  639. }
  640. // Get FIRMessaging shared instance.
  641. IMP messagingInstanceIMP = [messagingClass methodForSelector:instanceSelector];
  642. id (*getMessagingInstance)(id, SEL) = (void *)messagingInstanceIMP;
  643. id messagingInstance = getMessagingInstance(messagingClass, instanceSelector);
  644. // Messaging doesn't have the property, auto init should be enabled since FCM exists.
  645. SEL autoInitSelector = NSSelectorFromString(kFIRInstanceIDFCMSelectorAutoInitEnabled);
  646. if (![messagingInstance respondsToSelector:autoInitSelector]) {
  647. return YES;
  648. }
  649. // Get autoInitEnabled method.
  650. IMP isAutoInitEnabledIMP = [messagingInstance methodForSelector:autoInitSelector];
  651. BOOL (*isAutoInitEnabled)(id, SEL) = (BOOL(*)(id, SEL))isAutoInitEnabledIMP;
  652. // Check FCM's isAutoInitEnabled property.
  653. return isAutoInitEnabled(messagingInstance, autoInitSelector);
  654. }
  655. // Actually makes InstanceID instantiate both the IID and Token-related subsystems.
  656. - (void)start {
  657. if (![FIRInstanceIDStore hasSubDirectory:kFIRInstanceIDSubDirectoryName]) {
  658. [FIRInstanceIDStore createSubDirectory:kFIRInstanceIDSubDirectoryName];
  659. }
  660. [self setupTokenManager];
  661. [self setupKeyPairManager];
  662. [self setupNotificationListeners];
  663. }
  664. // Creates the token manager, which is used for fetching, caching, and retrieving tokens.
  665. - (void)setupTokenManager {
  666. self.tokenManager = [[FIRInstanceIDTokenManager alloc] init];
  667. }
  668. // Creates a key pair manager, which stores the public/private keys needed to generate an
  669. // application instance ID.
  670. - (void)setupKeyPairManager {
  671. self.keyPairStore = [[FIRInstanceIDKeyPairStore alloc] init];
  672. if ([self.keyPairStore invalidateKeyPairsIfNeeded]) {
  673. // Reset tokens right away when keypair is deleted, otherwise async call can make first query
  674. // of token happens before reset old tokens during app start.
  675. // TODO(chliangGoogle): Delete all tokens on server too, using
  676. // deleteAllTokensWithKeyPair:handler:. This requires actually retrieving the invalid keypair
  677. // from Keychain, which is something that the key pair store does not currently do.
  678. [self.tokenManager deleteAllTokensLocallyWithHandler:nil];
  679. }
  680. }
  681. - (void)setupNotificationListeners {
  682. // To prevent double notifications remove observer from all events during setup.
  683. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  684. [center removeObserver:self];
  685. [center addObserver:self
  686. selector:@selector(notifyIdentityReset)
  687. name:kFIRInstanceIDIdentityInvalidatedNotification
  688. object:nil];
  689. [center addObserver:self
  690. selector:@selector(notifyAPNSTokenIsSet:)
  691. name:kFIRInstanceIDAPNSTokenNotification
  692. object:nil];
  693. }
  694. #pragma mark - Private Helpers
  695. /// Maximum retry count to fetch the default token.
  696. + (int64_t)maxRetryCountForDefaultToken {
  697. return kMaxRetryCountForDefaultToken;
  698. }
  699. /// Minimum interval in seconds between retries to fetch the default token.
  700. + (int64_t)minIntervalForDefaultTokenRetry {
  701. return kMinRetryIntervalForDefaultTokenInSeconds;
  702. }
  703. /// Maximum retry interval between retries to fetch default token.
  704. + (int64_t)maxRetryIntervalForDefaultTokenInSeconds {
  705. return kMaxRetryIntervalForDefaultTokenInSeconds;
  706. }
  707. - (NSInteger)retryIntervalToFetchDefaultToken {
  708. if (self.retryCountForDefaultToken >= [[self class] maxRetryCountForDefaultToken]) {
  709. return (NSInteger)[[self class] maxRetryIntervalForDefaultTokenInSeconds];
  710. }
  711. // exponential backoff with a fixed initial retry time
  712. // 11s, 22s, 44s, 88s ...
  713. int64_t minInterval = [[self class] minIntervalForDefaultTokenRetry];
  714. return (NSInteger)MIN(
  715. (1 << self.retryCountForDefaultToken) + minInterval * self.retryCountForDefaultToken,
  716. kMaxRetryIntervalForDefaultTokenInSeconds);
  717. }
  718. - (void)defaultTokenWithHandler:(nullable FIRInstanceIDTokenHandler)aHandler {
  719. [self defaultTokenWithRetry:NO handler:aHandler];
  720. }
  721. /**
  722. * @param retry Indicates if the method is called to perform a retry after a failed attempt.
  723. * If `YES`, then actual token request will be performed even if `self.defaultTokenFetchHandler !=
  724. * nil`
  725. */
  726. - (void)defaultTokenWithRetry:(BOOL)retry handler:(nullable FIRInstanceIDTokenHandler)aHandler {
  727. BOOL shouldPerformRequest = retry || self.defaultTokenFetchHandler == nil;
  728. if (!self.defaultTokenFetchHandler) {
  729. self.defaultTokenFetchHandler = [[FIRInstanceIDCombinedHandler<NSString *> alloc] init];
  730. }
  731. if (aHandler) {
  732. [self.defaultTokenFetchHandler addHandler:aHandler];
  733. }
  734. if (!shouldPerformRequest) {
  735. return;
  736. }
  737. NSDictionary *instanceIDOptions = @{};
  738. BOOL hasFirebaseMessaging = NSClassFromString(kFIRInstanceIDFCMSDKClassString) != nil;
  739. if (hasFirebaseMessaging && self.apnsTokenData) {
  740. BOOL isSandboxApp = (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeSandbox);
  741. if (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeUnknown) {
  742. isSandboxApp = [self isSandboxApp];
  743. }
  744. instanceIDOptions = @{
  745. kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData,
  746. kFIRInstanceIDTokenOptionsAPNSIsSandboxKey : @(isSandboxApp),
  747. };
  748. }
  749. FIRInstanceID_WEAKIFY(self);
  750. FIRInstanceIDTokenHandler newHandler = ^void(NSString *token, NSError *error) {
  751. FIRInstanceID_STRONGIFY(self);
  752. if (error) {
  753. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID009,
  754. @"Failed to fetch default token %@", error);
  755. // This notification can be sent multiple times since we can't guarantee success at any point
  756. // of time.
  757. NSNotification *tokenFetchFailNotification =
  758. [NSNotification notificationWithName:kFIRInstanceIDDefaultGCMTokenFailNotification
  759. object:[error copy]];
  760. [[NSNotificationQueue defaultQueue] enqueueNotification:tokenFetchFailNotification
  761. postingStyle:NSPostASAP];
  762. self.retryCountForDefaultToken = (NSInteger)MIN(self.retryCountForDefaultToken + 1,
  763. [[self class] maxRetryCountForDefaultToken]);
  764. // Do not retry beyond the maximum limit.
  765. if (self.retryCountForDefaultToken < [[self class] maxRetryCountForDefaultToken]) {
  766. NSInteger retryInterval = [self retryIntervalToFetchDefaultToken];
  767. [self retryGetDefaultTokenAfter:retryInterval];
  768. } else {
  769. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007,
  770. @"Failed to retrieve the default FCM token after %ld retries",
  771. (long)self.retryCountForDefaultToken);
  772. [self performDefaultTokenHandlerWithToken:nil error:error];
  773. }
  774. } else {
  775. // If somebody updated IID with APNS token while our initial request did not have it
  776. // set we need to update it on the server.
  777. NSData *deviceTokenInRequest = instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSKey];
  778. BOOL isSandboxInRequest =
  779. [instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSIsSandboxKey] boolValue];
  780. // Note that APNSTupleStringInRequest will be nil if deviceTokenInRequest is nil
  781. NSString *APNSTupleStringInRequest = FIRInstanceIDAPNSTupleStringForTokenAndServerType(
  782. deviceTokenInRequest, isSandboxInRequest);
  783. // If the APNs value either remained nil, or was the same non-nil value, the APNs value
  784. // did not change.
  785. BOOL APNSRemainedSameDuringFetch =
  786. (self.APNSTupleString == nil && APNSTupleStringInRequest == nil) ||
  787. ([self.APNSTupleString isEqualToString:APNSTupleStringInRequest]);
  788. if (!APNSRemainedSameDuringFetch && hasFirebaseMessaging) {
  789. // APNs value did change mid-fetch, so the token should be re-fetched with the current APNs
  790. // value.
  791. [self retryGetDefaultTokenAfter:0];
  792. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeRefetchingTokenForAPNS,
  793. @"Received APNS token while fetching default token. "
  794. @"Refetching default token.");
  795. // Do not notify and handle completion handler since this is a retry.
  796. // Simply return.
  797. return;
  798. } else {
  799. FIRInstanceIDLoggerInfo(kFIRInstanceIDMessageCodeInstanceID010,
  800. @"Successfully fetched default token.");
  801. }
  802. // Post the required notifications if somebody is waiting.
  803. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@",
  804. token);
  805. NSString *previousFCMToken = self.defaultFCMToken;
  806. self.defaultFCMToken = token;
  807. // Only notify of token refresh if we have a new valid token that's different than before
  808. if (self.defaultFCMToken.length && ![self.defaultFCMToken isEqualToString:previousFCMToken]) {
  809. NSNotification *tokenRefreshNotification =
  810. [NSNotification notificationWithName:kFIRInstanceIDTokenRefreshNotification
  811. object:[self.defaultFCMToken copy]];
  812. [[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification
  813. postingStyle:NSPostASAP];
  814. [self performDefaultTokenHandlerWithToken:token error:nil];
  815. }
  816. }
  817. };
  818. [self tokenWithAuthorizedEntity:self.fcmSenderID
  819. scope:kFIRInstanceIDDefaultTokenScope
  820. options:instanceIDOptions
  821. handler:newHandler];
  822. }
  823. /**
  824. *
  825. */
  826. - (void)performDefaultTokenHandlerWithToken:(NSString *)token error:(NSError *)error {
  827. if (!self.defaultTokenFetchHandler) {
  828. return;
  829. }
  830. [self.defaultTokenFetchHandler combinedHandler](token, error);
  831. self.defaultTokenFetchHandler = nil;
  832. }
  833. - (void)retryGetDefaultTokenAfter:(NSTimeInterval)retryInterval {
  834. FIRInstanceID_WEAKIFY(self);
  835. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryInterval * NSEC_PER_SEC)),
  836. dispatch_get_main_queue(), ^{
  837. FIRInstanceID_STRONGIFY(self);
  838. // Pass nil: no new handlers to be added, currently existing handlers
  839. // will be called
  840. [self defaultTokenWithRetry:YES handler:nil];
  841. });
  842. }
  843. #pragma mark - APNS Token
  844. // This should only be triggered from FCM.
  845. - (void)notifyAPNSTokenIsSet:(NSNotification *)notification {
  846. NSData *token = notification.object;
  847. if (!token || ![token isKindOfClass:[NSData class]]) {
  848. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInternal002, @"Invalid APNS token type %@",
  849. NSStringFromClass([notification.object class]));
  850. return;
  851. }
  852. NSInteger type = [notification.userInfo[kFIRInstanceIDAPNSTokenType] integerValue];
  853. // The APNS token is being added, or has changed (rare)
  854. if ([self.apnsTokenData isEqualToData:token]) {
  855. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID011,
  856. @"Trying to reset APNS token to the same value. Will return");
  857. return;
  858. }
  859. // Use this token type for when we have to automatically fetch tokens in the future
  860. self.apnsTokenType = type;
  861. BOOL isSandboxApp = (type == FIRInstanceIDAPNSTokenTypeSandbox);
  862. if (self.apnsTokenType == FIRInstanceIDAPNSTokenTypeUnknown) {
  863. isSandboxApp = [self isSandboxApp];
  864. }
  865. self.apnsTokenData = [token copy];
  866. self.APNSTupleString = FIRInstanceIDAPNSTupleStringForTokenAndServerType(token, isSandboxApp);
  867. // Pro-actively invalidate the default token, if the APNs change makes it
  868. // invalid. Previously, we invalidated just before fetching the token.
  869. NSArray<FIRInstanceIDTokenInfo *> *invalidatedTokens =
  870. [self.tokenManager updateTokensToAPNSDeviceToken:self.apnsTokenData isSandbox:isSandboxApp];
  871. // Re-fetch any invalidated tokens automatically, this time with the current APNs token, so that
  872. // they are up-to-date.
  873. if (invalidatedTokens.count > 0) {
  874. FIRInstanceID_WEAKIFY(self);
  875. [self asyncLoadKeyPairWithHandler:^(FIRInstanceIDKeyPair *keyPair, NSError *error) {
  876. FIRInstanceID_STRONGIFY(self);
  877. NSMutableDictionary *tokenOptions = [@{
  878. kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData,
  879. kFIRInstanceIDTokenOptionsAPNSIsSandboxKey : @(isSandboxApp)
  880. } mutableCopy];
  881. if (self.firebaseAppID) {
  882. tokenOptions[kFIRInstanceIDTokenOptionsFirebaseAppIDKey] = self.firebaseAppID;
  883. }
  884. for (FIRInstanceIDTokenInfo *tokenInfo in invalidatedTokens) {
  885. if ([tokenInfo.token isEqualToString:self.defaultFCMToken]) {
  886. // We will perform a special fetch for the default FCM token, so that the delegate methods
  887. // are called. For all others, we will do an internal re-fetch.
  888. [self defaultTokenWithHandler:nil];
  889. } else {
  890. [self.tokenManager fetchNewTokenWithAuthorizedEntity:tokenInfo.authorizedEntity
  891. scope:tokenInfo.scope
  892. keyPair:keyPair
  893. options:tokenOptions
  894. handler:^(NSString *_Nullable token,
  895. NSError *_Nullable error){
  896. }];
  897. }
  898. }
  899. }];
  900. }
  901. }
  902. - (BOOL)isSandboxApp {
  903. static BOOL isSandboxApp = YES;
  904. static dispatch_once_t onceToken;
  905. dispatch_once(&onceToken, ^{
  906. isSandboxApp = ![self isProductionApp];
  907. });
  908. return isSandboxApp;
  909. }
  910. - (BOOL)isProductionApp {
  911. const BOOL defaultAppTypeProd = YES;
  912. NSError *error = nil;
  913. Class envClass = NSClassFromString(@"FIRAppEnvironmentUtil");
  914. SEL isSimulatorSelector = NSSelectorFromString(@"isSimulator");
  915. if ([envClass respondsToSelector:isSimulatorSelector]) {
  916. #pragma clang diagnostic push
  917. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  918. if ([envClass performSelector:isSimulatorSelector]) {
  919. #pragma clang diagnostic pop
  920. [self logAPNSConfigurationError:@"Running InstanceID on a simulator doesn't have APNS. "
  921. @"Use prod profile by default."];
  922. return defaultAppTypeProd;
  923. }
  924. }
  925. NSString *path = [[[NSBundle mainBundle] bundlePath]
  926. stringByAppendingPathComponent:@"embedded.mobileprovision"];
  927. // Apps distributed via AppStore or TestFlight use the Production APNS certificates.
  928. SEL isFromAppStoreSelector = NSSelectorFromString(@"isFromAppStore");
  929. if ([envClass respondsToSelector:isFromAppStoreSelector]) {
  930. #pragma clang diagnostic push
  931. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  932. if ([envClass performSelector:isFromAppStoreSelector]) {
  933. #pragma clang diagnostic pop
  934. return defaultAppTypeProd;
  935. }
  936. }
  937. SEL isAppStoreReceiptSandboxSelector = NSSelectorFromString(@"isAppStoreReceiptSandbox");
  938. if ([envClass respondsToSelector:isAppStoreReceiptSandboxSelector]) {
  939. #pragma clang diagnostic push
  940. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  941. if ([envClass performSelector:isAppStoreReceiptSandboxSelector] && !path.length) {
  942. #pragma clang diagnostic pop
  943. // Distributed via TestFlight
  944. return defaultAppTypeProd;
  945. }
  946. }
  947. NSMutableData *profileData = [NSMutableData dataWithContentsOfFile:path options:0 error:&error];
  948. if (!profileData.length || error) {
  949. NSString *errorString =
  950. [NSString stringWithFormat:@"Error while reading embedded mobileprovision %@", error];
  951. [self logAPNSConfigurationError:errorString];
  952. return defaultAppTypeProd;
  953. }
  954. // The "embedded.mobileprovision" sometimes contains characters with value 0, which signals the
  955. // end of a c-string and halts the ASCII parser, or with value > 127, which violates strict 7-bit
  956. // ASCII. Replace any 0s or invalid characters in the input.
  957. uint8_t *profileBytes = (uint8_t *)profileData.bytes;
  958. for (int i = 0; i < profileData.length; i++) {
  959. uint8_t currentByte = profileBytes[i];
  960. if (!currentByte || currentByte > 127) {
  961. profileBytes[i] = '.';
  962. }
  963. }
  964. NSString *embeddedProfile = [[NSString alloc] initWithBytesNoCopy:profileBytes
  965. length:profileData.length
  966. encoding:NSASCIIStringEncoding
  967. freeWhenDone:NO];
  968. if (error || !embeddedProfile.length) {
  969. NSString *errorString =
  970. [NSString stringWithFormat:@"Error while reading embedded mobileprovision %@", error];
  971. [self logAPNSConfigurationError:errorString];
  972. return defaultAppTypeProd;
  973. }
  974. NSScanner *scanner = [NSScanner scannerWithString:embeddedProfile];
  975. NSString *plistContents;
  976. if ([scanner scanUpToString:@"<plist" intoString:nil]) {
  977. if ([scanner scanUpToString:@"</plist>" intoString:&plistContents]) {
  978. plistContents = [plistContents stringByAppendingString:@"</plist>"];
  979. }
  980. }
  981. if (!plistContents.length) {
  982. return defaultAppTypeProd;
  983. }
  984. NSData *data = [plistContents dataUsingEncoding:NSUTF8StringEncoding];
  985. if (!data.length) {
  986. [self logAPNSConfigurationError:@"Couldn't read plist fetched from embedded mobileprovision"];
  987. return defaultAppTypeProd;
  988. }
  989. NSError *plistMapError;
  990. id plistData = [NSPropertyListSerialization propertyListWithData:data
  991. options:NSPropertyListImmutable
  992. format:nil
  993. error:&plistMapError];
  994. if (plistMapError || ![plistData isKindOfClass:[NSDictionary class]]) {
  995. NSString *errorString =
  996. [NSString stringWithFormat:@"Error while converting assumed plist to dict %@",
  997. plistMapError.localizedDescription];
  998. [self logAPNSConfigurationError:errorString];
  999. return defaultAppTypeProd;
  1000. }
  1001. NSDictionary *plistMap = (NSDictionary *)plistData;
  1002. if ([plistMap valueForKeyPath:@"ProvisionedDevices"]) {
  1003. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID012,
  1004. @"Provisioning profile has specifically provisioned devices, "
  1005. @"most likely a Dev profile.");
  1006. }
  1007. NSString *apsEnvironment = [plistMap valueForKeyPath:kEntitlementsAPSEnvironmentKey];
  1008. NSString *debugString __unused =
  1009. [NSString stringWithFormat:@"APNS Environment in profile: %@", apsEnvironment];
  1010. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID013, @"%@", debugString);
  1011. // No aps-environment in the profile.
  1012. if (!apsEnvironment.length) {
  1013. [self logAPNSConfigurationError:@"No aps-environment set. If testing on a device APNS is not "
  1014. @"correctly configured. Please recheck your provisioning "
  1015. @"profiles. If testing on a simulator this is fine since APNS "
  1016. @"doesn't work on the simulator."];
  1017. return defaultAppTypeProd;
  1018. }
  1019. if ([apsEnvironment isEqualToString:kAPSEnvironmentDevelopmentValue]) {
  1020. return NO;
  1021. }
  1022. return defaultAppTypeProd;
  1023. }
  1024. /// Log error messages only when Messaging exists in the pod.
  1025. - (void)logAPNSConfigurationError:(NSString *)errorString {
  1026. BOOL hasFirebaseMessaging = NSClassFromString(kFIRInstanceIDFCMSDKClassString) != nil;
  1027. if (hasFirebaseMessaging) {
  1028. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID014, @"%@", errorString);
  1029. } else {
  1030. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID015, @"%@", errorString);
  1031. }
  1032. }
  1033. @end