FIRAppCheckAPIService.m 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * Copyright 2020 Google LLC
  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 "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h"
  17. #if __has_include(<FBLPromises/FBLPromises.h>)
  18. #import <FBLPromises/FBLPromises.h>
  19. #else
  20. #import "FBLPromises.h"
  21. #endif
  22. #import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckToken+APIResponse.h"
  23. #import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
  24. #import "FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h"
  25. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  26. #import <GoogleUtilities/GULURLSessionDataResponse.h>
  27. #import <GoogleUtilities/NSURLSession+GULPromises.h>
  28. NS_ASSUME_NONNULL_BEGIN
  29. static NSString *const kAPIKeyHeaderKey = @"X-Goog-Api-Key";
  30. static NSString *const kHeartbeatKey = @"X-firebase-client-log-type";
  31. static NSString *const kHeartbeatStorageTag = @"fire-app-check";
  32. static NSString *const kUserAgentKey = @"X-firebase-client";
  33. static NSString *const kBundleIdKey = @"X-Ios-Bundle-Identifier";
  34. static NSString *const kDefaultBaseURL = @"https://firebaseappcheck.googleapis.com/v1beta";
  35. @interface FIRAppCheckAPIService ()
  36. @property(nonatomic, readonly) NSURLSession *URLSession;
  37. @property(nonatomic, readonly) NSString *APIKey;
  38. @property(nonatomic, readonly) NSString *projectID;
  39. @property(nonatomic, readonly) NSString *appID;
  40. @end
  41. @implementation FIRAppCheckAPIService
  42. // Synthesize properties declared in a protocol.
  43. @synthesize baseURL = _baseURL;
  44. - (instancetype)initWithURLSession:(NSURLSession *)session
  45. APIKey:(NSString *)APIKey
  46. projectID:(NSString *)projectID
  47. appID:(NSString *)appID {
  48. return [self initWithURLSession:session
  49. APIKey:APIKey
  50. projectID:projectID
  51. appID:appID
  52. baseURL:kDefaultBaseURL];
  53. }
  54. - (instancetype)initWithURLSession:(NSURLSession *)session
  55. APIKey:(NSString *)APIKey
  56. projectID:(NSString *)projectID
  57. appID:(NSString *)appID
  58. baseURL:(NSString *)baseURL {
  59. self = [super init];
  60. if (self) {
  61. _URLSession = session;
  62. _APIKey = APIKey;
  63. _projectID = projectID;
  64. _appID = appID;
  65. _baseURL = baseURL;
  66. }
  67. return self;
  68. }
  69. - (FBLPromise<GULURLSessionDataResponse *> *)
  70. sendRequestWithURL:(NSURL *)requestURL
  71. HTTPMethod:(NSString *)HTTPMethod
  72. body:(nullable NSData *)body
  73. additionalHeaders:(nullable NSDictionary<NSString *, NSString *> *)additionalHeaders {
  74. return [self requestWithURL:requestURL
  75. HTTPMethod:HTTPMethod
  76. body:body
  77. additionalHeaders:additionalHeaders]
  78. .then(^id _Nullable(NSURLRequest *_Nullable request) {
  79. return [self sendURLRequest:request];
  80. })
  81. .then(^id _Nullable(GULURLSessionDataResponse *_Nullable response) {
  82. return [self validateHTTPResponseStatusCode:response];
  83. });
  84. }
  85. - (FBLPromise<NSURLRequest *> *)requestWithURL:(NSURL *)requestURL
  86. HTTPMethod:(NSString *)HTTPMethod
  87. body:(NSData *)body
  88. additionalHeaders:(nullable NSDictionary<NSString *, NSString *> *)
  89. additionalHeaders {
  90. return [FBLPromise
  91. onQueue:[self defaultQueue]
  92. do:^id _Nullable {
  93. __block NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
  94. request.HTTPMethod = HTTPMethod;
  95. request.HTTPBody = body;
  96. [request setValue:self.APIKey forHTTPHeaderField:kAPIKeyHeaderKey];
  97. [request setValue:[FIRApp firebaseUserAgent] forHTTPHeaderField:kUserAgentKey];
  98. [request setValue:@([FIRHeartbeatInfo heartbeatCodeForTag:kHeartbeatStorageTag])
  99. .stringValue
  100. forHTTPHeaderField:kHeartbeatKey];
  101. [request setValue:[[NSBundle mainBundle] bundleIdentifier]
  102. forHTTPHeaderField:kBundleIdKey];
  103. [additionalHeaders
  104. enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, NSString *_Nonnull obj,
  105. BOOL *_Nonnull stop) {
  106. [request setValue:obj forHTTPHeaderField:key];
  107. }];
  108. return [request copy];
  109. }];
  110. }
  111. - (FBLPromise<GULURLSessionDataResponse *> *)sendURLRequest:(NSURLRequest *)request {
  112. return [self.URLSession gul_dataTaskPromiseWithRequest:request]
  113. .recover(^id(NSError *networkError) {
  114. // Wrap raw network error into App Check domain error.
  115. return [FIRAppCheckErrorUtil APIErrorWithNetworkError:networkError];
  116. })
  117. .then(^id _Nullable(GULURLSessionDataResponse *response) {
  118. return [self validateHTTPResponseStatusCode:response];
  119. });
  120. }
  121. - (FBLPromise<GULURLSessionDataResponse *> *)validateHTTPResponseStatusCode:
  122. (GULURLSessionDataResponse *)response {
  123. NSInteger statusCode = response.HTTPResponse.statusCode;
  124. return [FBLPromise do:^id _Nullable {
  125. if (statusCode < 200 || statusCode >= 300) {
  126. FIRAppCheckDebugLog(kFIRLoggerAppCheckMessageCodeUnexpectedHTTPCode,
  127. @"Unexpected API response: %@, body: %@.", response.HTTPResponse,
  128. [[NSString alloc] initWithData:response.HTTPBody
  129. encoding:NSUTF8StringEncoding]);
  130. return [FIRAppCheckErrorUtil APIErrorWithHTTPResponse:response.HTTPResponse
  131. data:response.HTTPBody];
  132. }
  133. return response;
  134. }];
  135. }
  136. - (FBLPromise<FIRAppCheckToken *> *)appCheckTokenWithAPIResponse:
  137. (GULURLSessionDataResponse *)response {
  138. return [FBLPromise onQueue:[self defaultQueue]
  139. do:^id _Nullable {
  140. NSError *error;
  141. FIRAppCheckToken *token = [[FIRAppCheckToken alloc]
  142. initWithTokenExchangeResponse:response.HTTPBody
  143. requestDate:[NSDate date]
  144. error:&error];
  145. return token ?: error;
  146. }];
  147. }
  148. - (dispatch_queue_t)defaultQueue {
  149. return dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
  150. }
  151. @end
  152. NS_ASSUME_NONNULL_END