FIRInstanceIDTokenOperation.m 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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 "Firebase/InstanceID/FIRInstanceIDTokenOperation.h"
  17. #import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
  18. #import "Firebase/InstanceID/FIRInstanceIDLogger.h"
  19. #import "Firebase/InstanceID/FIRInstanceIDUtilities.h"
  20. #import "Firebase/InstanceID/NSError+FIRInstanceID.h"
  21. #import "Firebase/InstanceID/Private/FIRInstanceIDCheckinPreferences.h"
  22. static const NSInteger kFIRInstanceIDPlatformVersionIOS = 2;
  23. // Scope parameter that defines the service using the token
  24. static NSString *const kFIRInstanceIDParamScope = @"X-scope";
  25. // Defines the SDK version
  26. static NSString *const kFIRInstanceIDParamFCMLibVersion = @"X-cliv";
  27. @interface FIRInstanceIDTokenOperation () {
  28. BOOL _isFinished;
  29. BOOL _isExecuting;
  30. }
  31. @property(nonatomic, readwrite, strong) FIRInstanceIDCheckinPreferences *checkinPreferences;
  32. @property(nonatomic, readwrite, strong) NSString *instanceID;
  33. @property(atomic, strong) NSURLSessionDataTask *dataTask;
  34. @property(readonly, strong)
  35. NSMutableArray<FIRInstanceIDTokenOperationCompletion> *completionHandlers;
  36. @property(atomic, strong, nullable) NSString *FISAuthToken;
  37. // For testing only
  38. @property(nonatomic, readwrite, copy) FIRInstanceIDURLRequestTestBlock testBlock;
  39. @end
  40. @implementation FIRInstanceIDTokenOperation
  41. + (NSURLSession *)sharedURLSession {
  42. static NSURLSession *tokenOperationSharedSession;
  43. static dispatch_once_t onceToken;
  44. dispatch_once(&onceToken, ^{
  45. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  46. config.timeoutIntervalForResource = 60.0f; // 1 minute
  47. tokenOperationSharedSession = [NSURLSession sessionWithConfiguration:config];
  48. tokenOperationSharedSession.sessionDescription = @"com.google.iid.tokens.session";
  49. });
  50. return tokenOperationSharedSession;
  51. }
  52. - (instancetype)initWithAction:(FIRInstanceIDTokenAction)action
  53. forAuthorizedEntity:(NSString *)authorizedEntity
  54. scope:(NSString *)scope
  55. options:(NSDictionary<NSString *, NSString *> *)options
  56. checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences
  57. instanceID:(NSString *)instanceID {
  58. self = [super init];
  59. if (self) {
  60. _action = action;
  61. _authorizedEntity = [authorizedEntity copy];
  62. _scope = [scope copy];
  63. _options = [options copy];
  64. _checkinPreferences = checkinPreferences;
  65. _instanceID = instanceID;
  66. _completionHandlers = [NSMutableArray array];
  67. _isExecuting = NO;
  68. _isFinished = NO;
  69. }
  70. return self;
  71. }
  72. - (void)dealloc {
  73. _testBlock = nil;
  74. _authorizedEntity = nil;
  75. _scope = nil;
  76. _options = nil;
  77. _checkinPreferences = nil;
  78. _instanceID = nil;
  79. [_completionHandlers removeAllObjects];
  80. _completionHandlers = nil;
  81. }
  82. - (void)addCompletionHandler:(FIRInstanceIDTokenOperationCompletion)handler {
  83. [self.completionHandlers addObject:handler];
  84. }
  85. - (BOOL)isAsynchronous {
  86. return YES;
  87. }
  88. - (BOOL)isExecuting {
  89. return _isExecuting;
  90. }
  91. - (void)setExecuting:(BOOL)executing {
  92. [self willChangeValueForKey:@"isExecuting"];
  93. _isExecuting = executing;
  94. [self didChangeValueForKey:@"isExecuting"];
  95. }
  96. - (BOOL)isFinished {
  97. return _isFinished;
  98. }
  99. - (void)setFinished:(BOOL)finished {
  100. [self willChangeValueForKey:@"isFinished"];
  101. _isFinished = finished;
  102. [self didChangeValueForKey:@"isFinished"];
  103. }
  104. - (void)start {
  105. if (self.isCancelled) {
  106. [self finishWithResult:FIRInstanceIDTokenOperationCancelled token:nil error:nil];
  107. return;
  108. }
  109. // Quickly validate whether or not the operation has all it needs to begin
  110. BOOL checkinfoAvailable = [self.checkinPreferences hasCheckinInfo];
  111. if (!checkinfoAvailable) {
  112. FIRInstanceIDErrorCode errorCode = kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn;
  113. [self finishWithResult:FIRInstanceIDTokenOperationError
  114. token:nil
  115. error:[NSError errorWithFIRInstanceIDErrorCode:errorCode]];
  116. return;
  117. }
  118. [self setExecuting:YES];
  119. [[FIRInstallations installations]
  120. authTokenWithCompletion:^(FIRInstallationsAuthTokenResult *_Nullable tokenResult,
  121. NSError *_Nullable error) {
  122. if (tokenResult.authToken.length > 0) {
  123. self.FISAuthToken = tokenResult.authToken;
  124. [self performTokenOperation];
  125. } else {
  126. [self finishWithResult:FIRInstanceIDTokenOperationError token:nil error:error];
  127. }
  128. }];
  129. }
  130. - (void)finishWithResult:(FIRInstanceIDTokenOperationResult)result
  131. token:(nullable NSString *)token
  132. error:(nullable NSError *)error {
  133. // Add a check to prevent this finish from being called more than once.
  134. if (self.isFinished) {
  135. return;
  136. }
  137. self.dataTask = nil;
  138. _result = result;
  139. // TODO(chliangGoogle): Call these in the main thread?
  140. for (FIRInstanceIDTokenOperationCompletion completionHandler in self.completionHandlers) {
  141. completionHandler(result, token, error);
  142. }
  143. [self setExecuting:NO];
  144. [self setFinished:YES];
  145. }
  146. - (void)cancel {
  147. [super cancel];
  148. [self.dataTask cancel];
  149. [self finishWithResult:FIRInstanceIDTokenOperationCancelled token:nil error:nil];
  150. }
  151. - (void)performTokenOperation {
  152. }
  153. - (NSMutableURLRequest *)tokenRequest {
  154. NSString *authHeader =
  155. [FIRInstanceIDTokenOperation HTTPAuthHeaderFromCheckin:self.checkinPreferences];
  156. return [[self class] requestWithAuthHeader:authHeader FISAuthToken:self.FISAuthToken];
  157. }
  158. #pragma mark - Request Construction
  159. + (NSMutableURLRequest *)requestWithAuthHeader:(NSString *)authHeaderString
  160. FISAuthToken:(NSString *)FISAuthToken {
  161. NSURL *url = [NSURL URLWithString:FIRInstanceIDRegisterServer()];
  162. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  163. // Add HTTP headers
  164. [request setValue:authHeaderString forHTTPHeaderField:@"Authorization"];
  165. [request setValue:FIRInstanceIDAppIdentifier() forHTTPHeaderField:@"app"];
  166. if (FISAuthToken) {
  167. [request setValue:FISAuthToken forHTTPHeaderField:@"x-goog-firebase-installations-auth"];
  168. }
  169. request.HTTPMethod = @"POST";
  170. return request;
  171. }
  172. + (NSMutableArray<NSURLQueryItem *> *)standardQueryItemsWithDeviceID:(NSString *)deviceID
  173. scope:(NSString *)scope {
  174. NSMutableArray<NSURLQueryItem *> *queryItems = [NSMutableArray arrayWithCapacity:8];
  175. // E.g. X-osv=10.2.1
  176. NSString *systemVersion = FIRInstanceIDOperatingSystemVersion();
  177. [queryItems addObject:[NSURLQueryItem queryItemWithName:@"X-osv" value:systemVersion]];
  178. // E.g. device=
  179. if (deviceID) {
  180. [queryItems addObject:[NSURLQueryItem queryItemWithName:@"device" value:deviceID]];
  181. }
  182. // E.g. X-scope=fcm
  183. if (scope) {
  184. [queryItems addObject:[NSURLQueryItem queryItemWithName:kFIRInstanceIDParamScope value:scope]];
  185. }
  186. // E.g. plat=2
  187. NSString *platform = [NSString stringWithFormat:@"%ld", (long)kFIRInstanceIDPlatformVersionIOS];
  188. [queryItems addObject:[NSURLQueryItem queryItemWithName:@"plat" value:platform]];
  189. // E.g. app=com.myapp.foo
  190. NSString *appIdentifier = FIRInstanceIDAppIdentifier();
  191. [queryItems addObject:[NSURLQueryItem queryItemWithName:@"app" value:appIdentifier]];
  192. // E.g. app_ver=1.5
  193. NSString *appVersion = FIRInstanceIDCurrentAppVersion();
  194. [queryItems addObject:[NSURLQueryItem queryItemWithName:@"app_ver" value:appVersion]];
  195. // E.g. X-cliv=fiid-1.2.3
  196. NSString *fcmLibraryVersion =
  197. [NSString stringWithFormat:@"fiid-%@", FIRInstanceIDCurrentGCMVersion()];
  198. if (fcmLibraryVersion.length) {
  199. NSURLQueryItem *gcmLibVersion =
  200. [NSURLQueryItem queryItemWithName:kFIRInstanceIDParamFCMLibVersion value:fcmLibraryVersion];
  201. [queryItems addObject:gcmLibVersion];
  202. }
  203. return queryItems;
  204. }
  205. #pragma mark - HTTP Header
  206. + (NSString *)HTTPAuthHeaderFromCheckin:(FIRInstanceIDCheckinPreferences *)checkin {
  207. NSString *deviceID = checkin.deviceID;
  208. NSString *secret = checkin.secretToken;
  209. return [NSString stringWithFormat:@"AidLogin %@:%@", deviceID, secret];
  210. }
  211. @end