You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

258 lines
8.9 KiB

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