Browse Source

Replace `FIRDeviceCheckProvider` with `GACDeviceCheckProvider` shim (#11434)

Re-implemented `FIRDeviceCheckProvider` as a shim around the non-Firebase-specific `GACDeviceCheckProvider`.
Andrew Heard 2 năm trước cách đây
mục cha
commit
69fe066604
34 tập tin đã thay đổi với 229 bổ sung2730 xóa
  1. 17 22
      AppCheck.podspec
  2. 1 3
      FirebaseAppCheck.podspec
  3. 0 58
      FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h
  4. 0 179
      FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.m
  5. 0 36
      FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckToken+APIResponse.h
  6. 0 93
      FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckToken+APIResponse.m
  7. 0 87
      FirebaseAppCheck/Sources/Core/Backoff/FIRAppCheckBackoffWrapper.h
  8. 0 287
      FirebaseAppCheck/Sources/Core/Backoff/FIRAppCheckBackoffWrapper.m
  9. 0 27
      FirebaseAppCheck/Sources/Core/Utils/FIRAppCheckCryptoUtils.h
  10. 0 29
      FirebaseAppCheck/Sources/Core/Utils/FIRAppCheckCryptoUtils.m
  11. 0 1
      FirebaseAppCheck/Sources/DebugProvider/FIRAppCheckDebugProvider.m
  12. 0 39
      FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h
  13. 0 117
      FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.m
  14. 0 33
      FirebaseAppCheck/Sources/DeviceCheckProvider/DCDevice+FIRDeviceCheckTokenGenerator.h
  15. 0 25
      FirebaseAppCheck/Sources/DeviceCheckProvider/DCDevice+FIRDeviceCheckTokenGenerator.m
  16. 22 94
      FirebaseAppCheck/Sources/DeviceCheckProvider/FIRDeviceCheckProvider.m
  17. 0 30
      FirebaseAppCheck/Sources/DeviceCheckProvider/FIRDeviceCheckTokenGenerator.h
  18. 0 7
      FirebaseAppCheck/Tests/Fixture/AppAttestAttestationResponseSuccess.json
  19. 0 3
      FirebaseAppCheck/Tests/Fixture/AppAttestResponseMissingChallenge.json
  20. 0 4
      FirebaseAppCheck/Tests/Fixture/AppAttestResponseSuccess.json
  21. 0 3
      FirebaseAppCheck/Tests/Fixture/DeviceCheckResponseMissingTimeToLive.json
  22. 0 3
      FirebaseAppCheck/Tests/Fixture/DeviceCheckResponseMissingToken.json
  23. 0 4
      FirebaseAppCheck/Tests/Fixture/FACTokenExchangeResponseSuccess.json
  24. 55 0
      FirebaseAppCheck/Tests/Integration/AppCheckE2ETests.swift
  25. 0 101
      FirebaseAppCheck/Tests/Integration/FIRDeviceCheckAPIServiceE2ETests.m
  26. 0 388
      FirebaseAppCheck/Tests/Unit/Core/FIRAppCheckAPIServiceTests.m
  27. 0 392
      FirebaseAppCheck/Tests/Unit/Core/FIRAppCheckBackoffWrapperTests.m
  28. 0 41
      FirebaseAppCheck/Tests/Unit/Core/FIRAppCheckCryptoUtilsTests.m
  29. 0 250
      FirebaseAppCheck/Tests/Unit/DeviceCheckProvider/FIRDeviceCheckAPIServiceTests.m
  30. 57 244
      FirebaseAppCheck/Tests/Unit/DeviceCheckProvider/FIRDeviceCheckProviderTests.m
  31. 9 11
      Package.swift
  32. 0 48
      SharedTestUtilities/AppCheckBackoffWrapperFake/FIRAppCheckBackoffWrapperFake.h
  33. 0 71
      SharedTestUtilities/AppCheckBackoffWrapperFake/FIRAppCheckBackoffWrapperFake.m
  34. 68 0
      scripts/spm_test_schemes/FirebaseAppCheckIntegration.xcscheme

+ 17 - 22
AppCheck.podspec

@@ -52,28 +52,23 @@ Pod::Spec.new do |s|
     'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}"'
   }
 
-  # Using environment variable because of the dependency on the unpublished
-  # HeartbeatLoggingTestUtils.
-  if ENV['POD_LIB_LINT_ONLY'] && ENV['POD_LIB_LINT_ONLY'] == '1' then
-    s.test_spec 'unit' do |unit_tests|
-      unit_tests.platforms = {
-        :ios => ios_deployment_target,
-        :osx => osx_deployment_target,
-        :tvos => tvos_deployment_target
-      }
-      unit_tests.source_files = [
-        base_dir + 'Tests/Unit/**/*.[mh]',
-        base_dir + 'Tests/Utils/**/*.[mh]',
-        'SharedTestUtilities/Date/*',
-        'SharedTestUtilities/URLSession/*',
-      ]
-
-      unit_tests.resources = base_dir + 'Tests/Fixture/**/*'
-      unit_tests.dependency 'FirebaseCore', '~> 10.0'
-      unit_tests.dependency 'OCMock'
-      unit_tests.dependency 'HeartbeatLoggingTestUtils'
-      unit_tests.requires_app_host = true
-    end
+  s.test_spec 'unit' do |unit_tests|
+    unit_tests.platforms = {
+      :ios => ios_deployment_target,
+      :osx => osx_deployment_target,
+      :tvos => tvos_deployment_target
+    }
+    unit_tests.source_files = [
+      base_dir + 'Tests/Unit/**/*.[mh]',
+      base_dir + 'Tests/Utils/**/*.[mh]',
+      'SharedTestUtilities/Date/*',
+      'SharedTestUtilities/URLSession/*',
+    ]
+
+    unit_tests.resources = base_dir + 'Tests/Fixture/**/*'
+    unit_tests.dependency 'FirebaseCore', '~> 10.0'
+    unit_tests.dependency 'OCMock'
+    unit_tests.requires_app_host = true
   end
 
   s.test_spec 'integration' do |integration_tests|

+ 1 - 3
FirebaseAppCheck.podspec

@@ -82,10 +82,8 @@ Pod::Spec.new do |s|
       :tvos => tvos_deployment_target
     }
     integration_tests.source_files = [
-      base_dir + 'Tests/Integration/**/*.[mh]',
-      base_dir + 'Tests/Integration/**/*.[mh]',
+      base_dir + 'Tests/Integration/**/*.swift',
     ]
-    integration_tests.resources = base_dir + 'Tests/Fixture/**/*'
     integration_tests.requires_app_host = true
   end
 

+ 0 - 58
FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h

@@ -1,58 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-@class FBLPromise<Result>;
-@class GULURLSessionDataResponse;
-@class FIRAppCheckToken;
-
-@protocol FIRHeartbeatLoggerProtocol;
-
-NS_ASSUME_NONNULL_BEGIN
-
-@protocol FIRAppCheckAPIServiceProtocol <NSObject>
-
-@property(nonatomic, readonly) NSString *baseURL;
-
-- (FBLPromise<GULURLSessionDataResponse *> *)
-    sendRequestWithURL:(NSURL *)requestURL
-            HTTPMethod:(NSString *)HTTPMethod
-                  body:(nullable NSData *)body
-     additionalHeaders:(nullable NSDictionary<NSString *, NSString *> *)additionalHeaders;
-
-- (FBLPromise<FIRAppCheckToken *> *)appCheckTokenWithAPIResponse:
-    (GULURLSessionDataResponse *)response;
-
-@end
-
-@interface FIRAppCheckAPIService : NSObject <FIRAppCheckAPIServiceProtocol>
-
-/**
- * The default initializer.
- * @param session The URL session used to make network requests.
- * @param APIKey The Firebase project API key (see `FIROptions.APIKey`).
- * @param appID The Firebase app ID (see `FIROptions.googleAppID`).
- * @param heartbeatLogger The heartbeat logger used to populate heartbeat data in request headers.
- */
-- (instancetype)initWithURLSession:(NSURLSession *)session
-                            APIKey:(NSString *)APIKey
-                             appID:(NSString *)appID
-                   heartbeatLogger:(id<FIRHeartbeatLoggerProtocol>)heartbeatLogger;
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 179
FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.m

@@ -1,179 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h"
-
-#if __has_include(<FBLPromises/FBLPromises.h>)
-#import <FBLPromises/FBLPromises.h>
-#else
-#import "FBLPromises.h"
-#endif
-
-#import <AppCheck/AppCheck.h>
-
-#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckToken+APIResponse.h"
-#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
-#import "FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h"
-
-#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
-
-#import <GoogleUtilities/GULURLSessionDataResponse.h>
-#import <GoogleUtilities/NSURLSession+GULPromises.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-static NSString *const kAPIKeyHeaderKey = @"X-Goog-Api-Key";
-static NSString *const kHeartbeatKey = @"X-firebase-client";
-static NSString *const kBundleIdKey = @"X-Ios-Bundle-Identifier";
-
-static NSString *const kDefaultBaseURL = @"https://firebaseappcheck.googleapis.com/v1";
-
-@interface FIRAppCheckAPIService ()
-
-@property(nonatomic, readonly) NSURLSession *URLSession;
-@property(nonatomic, readonly) NSString *APIKey;
-@property(nonatomic, readonly) NSString *appID;
-@property(nonatomic, readonly) id<FIRHeartbeatLoggerProtocol> heartbeatLogger;
-
-@end
-
-@implementation FIRAppCheckAPIService
-
-// Synthesize properties declared in a protocol.
-@synthesize baseURL = _baseURL;
-
-- (instancetype)initWithURLSession:(NSURLSession *)session
-                            APIKey:(NSString *)APIKey
-                             appID:(NSString *)appID
-                   heartbeatLogger:(id<FIRHeartbeatLoggerProtocol>)heartbeatLogger {
-  return [self initWithURLSession:session
-                           APIKey:APIKey
-                            appID:appID
-                  heartbeatLogger:heartbeatLogger
-                          baseURL:kDefaultBaseURL];
-}
-
-- (instancetype)initWithURLSession:(NSURLSession *)session
-                            APIKey:(NSString *)APIKey
-                             appID:(NSString *)appID
-                   heartbeatLogger:(id<FIRHeartbeatLoggerProtocol>)heartbeatLogger
-                           baseURL:(NSString *)baseURL {
-  self = [super init];
-  if (self) {
-    _URLSession = session;
-    _APIKey = APIKey;
-    _appID = appID;
-    _heartbeatLogger = heartbeatLogger;
-    _baseURL = baseURL;
-  }
-  return self;
-}
-
-- (FBLPromise<GULURLSessionDataResponse *> *)
-    sendRequestWithURL:(NSURL *)requestURL
-            HTTPMethod:(NSString *)HTTPMethod
-                  body:(nullable NSData *)body
-     additionalHeaders:(nullable NSDictionary<NSString *, NSString *> *)additionalHeaders {
-  return [self requestWithURL:requestURL
-                    HTTPMethod:HTTPMethod
-                          body:body
-             additionalHeaders:additionalHeaders]
-      .then(^id _Nullable(NSURLRequest *_Nullable request) {
-        return [self sendURLRequest:request];
-      })
-      .then(^id _Nullable(GULURLSessionDataResponse *_Nullable response) {
-        return [self validateHTTPResponseStatusCode:response];
-      });
-}
-
-- (FBLPromise<NSURLRequest *> *)requestWithURL:(NSURL *)requestURL
-                                    HTTPMethod:(NSString *)HTTPMethod
-                                          body:(NSData *)body
-                             additionalHeaders:(nullable NSDictionary<NSString *, NSString *> *)
-                                                   additionalHeaders {
-  return [FBLPromise
-      onQueue:[self defaultQueue]
-           do:^id _Nullable {
-             __block NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
-             request.HTTPMethod = HTTPMethod;
-             request.HTTPBody = body;
-
-             [request setValue:self.APIKey forHTTPHeaderField:kAPIKeyHeaderKey];
-
-             [request setValue:FIRHeaderValueFromHeartbeatsPayload(
-                                   [self.heartbeatLogger flushHeartbeatsIntoPayload])
-                 forHTTPHeaderField:kHeartbeatKey];
-
-             [request setValue:[[NSBundle mainBundle] bundleIdentifier]
-                 forHTTPHeaderField:kBundleIdKey];
-
-             [additionalHeaders
-                 enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, NSString *_Nonnull obj,
-                                                     BOOL *_Nonnull stop) {
-                   [request setValue:obj forHTTPHeaderField:key];
-                 }];
-
-             return [request copy];
-           }];
-}
-
-- (FBLPromise<GULURLSessionDataResponse *> *)sendURLRequest:(NSURLRequest *)request {
-  return [self.URLSession gul_dataTaskPromiseWithRequest:request]
-      .recover(^id(NSError *networkError) {
-        // Wrap raw network error into App Check domain error.
-        return [FIRAppCheckErrorUtil APIErrorWithNetworkError:networkError];
-      })
-      .then(^id _Nullable(GULURLSessionDataResponse *response) {
-        return [self validateHTTPResponseStatusCode:response];
-      });
-}
-
-- (FBLPromise<GULURLSessionDataResponse *> *)validateHTTPResponseStatusCode:
-    (GULURLSessionDataResponse *)response {
-  NSInteger statusCode = response.HTTPResponse.statusCode;
-  return [FBLPromise do:^id _Nullable {
-    if (statusCode < 200 || statusCode >= 300) {
-      FIRAppCheckDebugLog(kGACLoggerAppCheckMessageCodeUnexpectedHTTPCode,
-                          @"Unexpected API response: %@, body: %@.", response.HTTPResponse,
-                          [[NSString alloc] initWithData:response.HTTPBody
-                                                encoding:NSUTF8StringEncoding]);
-      return [FIRAppCheckErrorUtil APIErrorWithHTTPResponse:response.HTTPResponse
-                                                       data:response.HTTPBody];
-    }
-    return response;
-  }];
-}
-
-- (FBLPromise<FIRAppCheckToken *> *)appCheckTokenWithAPIResponse:
-    (GULURLSessionDataResponse *)response {
-  return [FBLPromise onQueue:[self defaultQueue]
-                          do:^id _Nullable {
-                            NSError *error;
-
-                            FIRAppCheckToken *token = [[FIRAppCheckToken alloc]
-                                initWithTokenExchangeResponse:response.HTTPBody
-                                                  requestDate:[NSDate date]
-                                                        error:&error];
-                            return token ?: error;
-                          }];
-}
-
-- (dispatch_queue_t)defaultQueue {
-  return dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
-}
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 36
FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckToken+APIResponse.h

@@ -1,36 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckToken.h"
-
-@class FBLPromise<Result>;
-@class GULURLSessionDataResponse;
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FIRAppCheckToken (APIResponse)
-
-- (nullable instancetype)initWithTokenExchangeResponse:(NSData *)response
-                                           requestDate:(NSDate *)requestDate
-                                                 error:(NSError **)outError;
-
-- (nullable instancetype)initWithResponseDict:(NSDictionary<NSString *, id> *)responseDict
-                                  requestDate:(NSDate *)requestDate
-                                        error:(NSError **)outError;
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 93
FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckToken+APIResponse.m

@@ -1,93 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckToken+APIResponse.h"
-#import "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.h"
-
-#if __has_include(<FBLPromises/FBLPromises.h>)
-#import <FBLPromises/FBLPromises.h>
-#else
-#import "FBLPromises.h"
-#endif
-
-#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
-
-#import <GoogleUtilities/GULURLSessionDataResponse.h>
-
-static NSString *const kResponseFieldToken = @"token";
-static NSString *const kResponseFieldTTL = @"ttl";
-
-@implementation FIRAppCheckToken (APIResponse)
-
-- (nullable instancetype)initWithTokenExchangeResponse:(NSData *)response
-                                           requestDate:(NSDate *)requestDate
-                                                 error:(NSError **)outError {
-  if (response.length <= 0) {
-    FIRAppCheckSetErrorToPointer(
-        [FIRAppCheckErrorUtil errorWithFailureReason:@"Empty server response body."], outError);
-    return nil;
-  }
-
-  NSError *JSONError;
-  NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:response
-                                                               options:0
-                                                                 error:&JSONError];
-
-  if (![responseDict isKindOfClass:[NSDictionary class]]) {
-    FIRAppCheckSetErrorToPointer([FIRAppCheckErrorUtil JSONSerializationError:JSONError], outError);
-    return nil;
-  }
-
-  return [self initWithResponseDict:responseDict requestDate:requestDate error:outError];
-}
-
-- (nullable instancetype)initWithResponseDict:(NSDictionary<NSString *, id> *)responseDict
-                                  requestDate:(NSDate *)requestDate
-                                        error:(NSError **)outError {
-  NSString *token = responseDict[kResponseFieldToken];
-  if (![token isKindOfClass:[NSString class]]) {
-    FIRAppCheckSetErrorToPointer(
-        [FIRAppCheckErrorUtil appCheckTokenResponseErrorWithMissingField:kResponseFieldToken],
-        outError);
-    return nil;
-  }
-
-  NSString *timeToLiveString = responseDict[kResponseFieldTTL];
-  if (![token isKindOfClass:[NSString class]] || token.length <= 0) {
-    FIRAppCheckSetErrorToPointer(
-        [FIRAppCheckErrorUtil appCheckTokenResponseErrorWithMissingField:kResponseFieldTTL],
-        outError);
-    return nil;
-  }
-
-  // Expect a string like "3600s" representing a time interval in seconds.
-  NSString *timeToLiveValueString = [timeToLiveString stringByReplacingOccurrencesOfString:@"s"
-                                                                                withString:@""];
-  NSTimeInterval secondsToLive = timeToLiveValueString.doubleValue;
-
-  if (secondsToLive == 0) {
-    FIRAppCheckSetErrorToPointer(
-        [FIRAppCheckErrorUtil appCheckTokenResponseErrorWithMissingField:kResponseFieldTTL],
-        outError);
-    return nil;
-  }
-
-  NSDate *expirationDate = [requestDate dateByAddingTimeInterval:secondsToLive];
-
-  return [self initWithToken:token expirationDate:expirationDate receivedAtDate:requestDate];
-}
-
-@end

+ 0 - 87
FirebaseAppCheck/Sources/Core/Backoff/FIRAppCheckBackoffWrapper.h

@@ -1,87 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-@class FBLPromise<ValueType>;
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// Backoff type. Backoff interval calculation depends on the type.
-typedef NS_ENUM(NSUInteger, FIRAppCheckBackoffType) {
-  /// No backoff. Another retry is allowed straight away.
-  FIRAppCheckBackoffTypeNone,
-
-  /// Next retry will be allowed in 1 day (24 hours) after the failure.
-  FIRAppCheckBackoffType1Day,
-
-  /// A small backoff interval that exponentially increases after each consequent failure.
-  FIRAppCheckBackoffTypeExponential
-};
-
-/// Creates a promise for an operation to apply the backoff to.
-typedef FBLPromise *_Nonnull (^FIRAppCheckBackoffOperationProvider)(void);
-
-/// Converts an error to a backoff type.
-typedef FIRAppCheckBackoffType (^FIRAppCheckBackoffErrorHandler)(NSError *error);
-
-/// A block returning a date. Is used instead of `+[NSDate date]` for better testability of logic
-/// dependent on the current time.
-typedef NSDate *_Nonnull (^FIRAppCheckDateProvider)(void);
-
-/// Defines API for an object that conditionally applies backoff to a given operation based on the
-/// history of previous operation failures.
-@protocol FIRAppCheckBackoffWrapperProtocol <NSObject>
-
-/// Conditionally applies backoff to the given operation.
-/// @param operationProvider A block that returns a new promise. The block will be called only when
-/// the operation is allowed.
-///        NOTE: We cannot accept just a promise because the operation will be started once the
-///        promise has been instantiated, so we need to have a way to instantiate the promise only
-///        when the operation is good to go. The provider block is the way we use.
-/// @param errorHandler A block that receives an operation error as an input and returns the
-/// appropriate backoff type. `defaultErrorHandler` provides a default implementation for Firebase
-/// services.
-/// @return A promise that is either:
-///   - a promise returned by the promise provider if no backoff is required
-///   - rejected if the backoff is needed
-- (FBLPromise *)applyBackoffToOperation:(FIRAppCheckBackoffOperationProvider)operationProvider
-                           errorHandler:(FIRAppCheckBackoffErrorHandler)errorHandler;
-
-/// The default Firebase services error handler. It keeps track of network errors and
-/// `FIRAppCheckHTTPError.HTTPResponse.statusCode.statusCode` value to return the appropriate
-/// backoff type for the standard Firebase App Check backend response codes.
-- (FIRAppCheckBackoffErrorHandler)defaultAppCheckProviderErrorHandler;
-
-@end
-
-/// Provides a backoff implementation. Keeps track of the operation successes and failures to either
-/// create and perform the operation promise or fails with a backoff error when the backoff is
-/// needed.
-@interface FIRAppCheckBackoffWrapper : NSObject <FIRAppCheckBackoffWrapperProtocol>
-
-/// Initializes the wrapper with `+[FIRAppCheckBackoffWrapper currentDateProvider]`.
-- (instancetype)init;
-
-- (instancetype)initWithDateProvider:(FIRAppCheckDateProvider)dateProvider
-    NS_DESIGNATED_INITIALIZER;
-
-/// A date provider that returns `+[NSDate date]`.
-+ (FIRAppCheckDateProvider)currentDateProvider;
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 287
FirebaseAppCheck/Sources/Core/Backoff/FIRAppCheckBackoffWrapper.m

@@ -1,287 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "FirebaseAppCheck/Sources/Core/Backoff/FIRAppCheckBackoffWrapper.h"
-
-#if __has_include(<FBLPromises/FBLPromises.h>)
-#import <FBLPromises/FBLPromises.h>
-#else
-#import "FBLPromises.h"
-#endif
-
-#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
-#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckHTTPError.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-static NSTimeInterval const k24Hours = 24 * 60 * 60;
-
-/// Jitter coefficient 0.5 means that the backoff interval can be up to 50% longer.
-static double const kMaxJitterCoefficient = 0.5;
-
-/// Maximum exponential backoff interval.
-static double const kMaxExponentialBackoffInterval = 4 * 60 * 60;  // 4 hours.
-
-/// A class representing an operation result with data required for the backoff calculation.
-@interface FIRAppCheckBackoffOperationFailure : NSObject
-
-/// The operation finish date.
-@property(nonatomic, readonly) NSDate *finishDate;
-
-/// The operation error.
-@property(nonatomic, readonly) NSError *error;
-
-/// A backoff type calculated based on the error.
-@property(nonatomic, readonly) FIRAppCheckBackoffType backoffType;
-
-/// Number of retries. Is 0 for the first attempt and incremented with each error. Is reset back to
-/// 0 on success.
-@property(nonatomic, readonly) NSInteger retryCount;
-
-/// Designated initializer.
-- (instancetype)initWithFinishDate:(NSDate *)finishDate
-                             error:(NSError *)error
-                       backoffType:(FIRAppCheckBackoffType)backoffType
-                        retryCount:(NSInteger)retryCount NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/// Creates a new result with incremented retryCount and specified error and backoff type.
-+ (instancetype)nextRetryFailureWithFailure:
-                    (nullable FIRAppCheckBackoffOperationFailure *)previousFailure
-                                 finishDate:(NSDate *)finishDate
-                                      error:(NSError *)error
-                                backoffType:(FIRAppCheckBackoffType)backoffType;
-
-@end
-
-@implementation FIRAppCheckBackoffOperationFailure
-
-- (instancetype)initWithFinishDate:(NSDate *)finishDate
-                             error:(NSError *)error
-                       backoffType:(FIRAppCheckBackoffType)backoffType
-                        retryCount:(NSInteger)retryCount {
-  self = [super init];
-  if (self) {
-    _finishDate = finishDate;
-    _error = error;
-    _retryCount = retryCount;
-    _backoffType = backoffType;
-  }
-  return self;
-}
-
-+ (instancetype)nextRetryFailureWithFailure:
-                    (nullable FIRAppCheckBackoffOperationFailure *)previousFailure
-                                 finishDate:(NSDate *)finishDate
-                                      error:(NSError *)error
-                                backoffType:(FIRAppCheckBackoffType)backoffType {
-  NSInteger newRetryCount = previousFailure ? previousFailure.retryCount + 1 : 0;
-
-  return [[self alloc] initWithFinishDate:finishDate
-                                    error:error
-                              backoffType:backoffType
-                               retryCount:newRetryCount];
-}
-
-@end
-
-@interface FIRAppCheckBackoffWrapper ()
-
-/// Current date provider. Is used instead of `+[NSDate date]` for testability.
-@property(nonatomic, readonly) FIRAppCheckDateProvider dateProvider;
-
-/// Last operation result.
-@property(nonatomic, nullable) FIRAppCheckBackoffOperationFailure *lastFailure;
-
-@end
-
-@implementation FIRAppCheckBackoffWrapper
-
-- (instancetype)init {
-  return [self initWithDateProvider:[FIRAppCheckBackoffWrapper currentDateProvider]];
-}
-
-- (instancetype)initWithDateProvider:(FIRAppCheckDateProvider)dateProvider {
-  self = [super init];
-  if (self) {
-    _dateProvider = [dateProvider copy];
-  }
-  return self;
-}
-
-+ (FIRAppCheckDateProvider)currentDateProvider {
-  return ^NSDate *(void) {
-    return [NSDate date];
-  };
-}
-
-- (FBLPromise *)applyBackoffToOperation:(FIRAppCheckBackoffOperationProvider)operationProvider
-                           errorHandler:(FIRAppCheckBackoffErrorHandler)errorHandler {
-  if (![self isNextOperationAllowed]) {
-    // Backing off - skip the operation and return an error straight away.
-    return [self promiseWithRetryDisallowedError:self.lastFailure.error];
-  }
-
-  __auto_type operationPromise = operationProvider();
-  return operationPromise
-      .thenOn([self queue],
-              ^id(id result) {
-                @synchronized(self) {
-                  // Reset failure on success.
-                  self.lastFailure = nil;
-                }
-
-                // Return the result.
-                return result;
-              })
-      .recoverOn([self queue], ^NSError *(NSError *error) {
-        @synchronized(self) {
-          // Update the last failure to calculate the backoff.
-          self.lastFailure =
-              [FIRAppCheckBackoffOperationFailure nextRetryFailureWithFailure:self.lastFailure
-                                                                   finishDate:self.dateProvider()
-                                                                        error:error
-                                                                  backoffType:errorHandler(error)];
-        }
-
-        // Re-throw the error.
-        return error;
-      });
-}
-
-#pragma mark - Private
-
-- (BOOL)isNextOperationAllowed {
-  @synchronized(self) {
-    if (self.lastFailure == nil) {
-      // It is first attempt. Always allow it.
-      return YES;
-    }
-
-    switch (self.lastFailure.backoffType) {
-      case FIRAppCheckBackoffTypeNone:
-        return YES;
-        break;
-
-      case FIRAppCheckBackoffType1Day:
-        return [self hasTimeIntervalPassedSinceLastFailure:k24Hours];
-        break;
-
-      case FIRAppCheckBackoffTypeExponential:
-        return [self hasTimeIntervalPassedSinceLastFailure:
-                         [self exponentialBackoffIntervalForFailure:self.lastFailure]];
-        break;
-    }
-  }
-}
-
-- (BOOL)hasTimeIntervalPassedSinceLastFailure:(NSTimeInterval)timeInterval {
-  NSDate *failureDate = self.lastFailure.finishDate;
-  // Return YES if there has not been a failure yet.
-  if (failureDate == nil) return YES;
-
-  NSTimeInterval timeSinceFailure = [self.dateProvider() timeIntervalSinceDate:failureDate];
-  return timeSinceFailure >= timeInterval;
-}
-
-- (FBLPromise *)promiseWithRetryDisallowedError:(NSError *)error {
-  NSString *reason =
-      [NSString stringWithFormat:@"Too many attempts. Underlying error: %@",
-                                 error.localizedDescription ?: error.localizedFailureReason];
-  NSError *retryDisallowedError = [FIRAppCheckErrorUtil errorWithFailureReason:reason];
-  FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
-  [rejectedPromise reject:retryDisallowedError];
-  return rejectedPromise;
-}
-
-- (dispatch_queue_t)queue {
-  return dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
-}
-
-#pragma mark - Exponential backoff
-
-/// @return Exponential backoff interval with jitter. Jitter is needed to avoid all clients to retry
-/// at the same time after e.g. a backend outage.
-- (NSTimeInterval)exponentialBackoffIntervalForFailure:
-    (FIRAppCheckBackoffOperationFailure *)failure {
-  // Base exponential backoff interval.
-  NSTimeInterval baseBackoff = pow(2, failure.retryCount);
-
-  // Get a random number from 0 to 1.
-  double maxRandom = 1000;
-  double randomNumber = (double)arc4random_uniform((int32_t)maxRandom) / maxRandom;
-
-  // A number from 1 to 1 + kMaxJitterCoefficient, e.g. from 1 to 1.5. Indicates how much the
-  // backoff can be extended.
-  double jitterCoefficient = 1 + randomNumber * kMaxJitterCoefficient;
-
-  // Exponential backoff interval with jitter.
-  NSTimeInterval backoffIntervalWithJitter = baseBackoff * jitterCoefficient;
-
-  // Apply limit to the backoff interval.
-  return MIN(backoffIntervalWithJitter, kMaxExponentialBackoffInterval);
-}
-
-#pragma mark - Error handling
-
-- (FIRAppCheckBackoffErrorHandler)defaultAppCheckProviderErrorHandler {
-  return ^FIRAppCheckBackoffType(NSError *error) {
-    FIRAppCheckHTTPError *HTTPError =
-        [error isKindOfClass:[FIRAppCheckHTTPError class]] ? (FIRAppCheckHTTPError *)error : nil;
-
-    if (HTTPError == nil) {
-      // No backoff for attestation providers for non-backend (e.g. network) errors.
-      return FIRAppCheckBackoffTypeNone;
-    }
-
-    NSInteger statusCode = HTTPError.HTTPResponse.statusCode;
-
-    if (statusCode < 400) {
-      // No backoff for codes before 400.
-      return FIRAppCheckBackoffTypeNone;
-    }
-
-    if (statusCode == 400 || statusCode == 404) {
-      // Firebase project misconfiguration. It will unlikely be fixed soon and often requires
-      // another version of the app. Try again in 1 day.
-      return FIRAppCheckBackoffType1Day;
-    }
-
-    if (statusCode == 403) {
-      // Project may have been soft-deleted accidentally. There is a chance of timely recovery, so
-      // try again later.
-      return FIRAppCheckBackoffTypeExponential;
-    }
-
-    if (statusCode == 429) {
-      // Too many requests. Try again in a while.
-      return FIRAppCheckBackoffTypeExponential;
-    }
-
-    if (statusCode == 503) {
-      // Server is overloaded. Try again in a while.
-      return FIRAppCheckBackoffTypeExponential;
-    }
-
-    // For all other server error cases default to the exponential backoff.
-    return FIRAppCheckBackoffTypeExponential;
-  };
-}
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 27
FirebaseAppCheck/Sources/Core/Utils/FIRAppCheckCryptoUtils.h

@@ -1,27 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FIRAppCheckCryptoUtils : NSObject
-
-+ (NSData *)sha256HashFromData:(NSData *)dataToHash;
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 29
FirebaseAppCheck/Sources/Core/Utils/FIRAppCheckCryptoUtils.m

@@ -1,29 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "FirebaseAppCheck/Sources/Core/Utils/FIRAppCheckCryptoUtils.h"
-
-#import <CommonCrypto/CommonDigest.h>
-
-@implementation FIRAppCheckCryptoUtils
-
-+ (NSData *)sha256HashFromData:(NSData *)dataToHash {
-  NSMutableData *digest = [[NSMutableData alloc] initWithLength:CC_SHA256_DIGEST_LENGTH];
-  CC_SHA256(dataToHash.bytes, (CC_LONG)dataToHash.length, digest.mutableBytes);
-  return [digest copy];
-}
-
-@end

+ 0 - 1
FirebaseAppCheck/Sources/DebugProvider/FIRAppCheckDebugProvider.m

@@ -37,7 +37,6 @@ NS_ASSUME_NONNULL_BEGIN
 @interface FIRAppCheckDebugProvider ()
 
 @property(nonatomic, readonly) GACAppCheckDebugProvider *debugProvider;
-@property(nonatomic, readonly) id<FIRHeartbeatLoggerProtocol> heartbeatLogger;
 
 @end
 

+ 0 - 39
FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h

@@ -1,39 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-@class FBLPromise<Result>;
-@class FIRAppCheckToken;
-@protocol FIRAppCheckAPIServiceProtocol;
-
-NS_ASSUME_NONNULL_BEGIN
-
-@protocol FIRDeviceCheckAPIServiceProtocol <NSObject>
-
-- (FBLPromise<FIRAppCheckToken *> *)appCheckTokenWithDeviceToken:(NSData *)deviceToken;
-
-@end
-
-@interface FIRDeviceCheckAPIService : NSObject <FIRDeviceCheckAPIServiceProtocol>
-
-- (instancetype)initWithAPIService:(id<FIRAppCheckAPIServiceProtocol>)APIService
-                         projectID:(NSString *)projectID
-                             appID:(NSString *)appID;
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 117
FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.m

@@ -1,117 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h"
-
-#if __has_include(<FBLPromises/FBLPromises.h>)
-#import <FBLPromises/FBLPromises.h>
-#else
-#import "FBLPromises.h"
-#endif
-
-#import <GoogleUtilities/GULURLSessionDataResponse.h>
-
-#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h"
-#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckToken+APIResponse.h"
-
-#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
-#import "FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h"
-
-#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-static NSString *const kContentTypeKey = @"Content-Type";
-static NSString *const kJSONContentType = @"application/json";
-static NSString *const kDeviceTokenField = @"device_token";
-
-@interface FIRDeviceCheckAPIService ()
-
-@property(nonatomic, readonly) id<FIRAppCheckAPIServiceProtocol> APIService;
-
-@property(nonatomic, readonly) NSString *projectID;
-@property(nonatomic, readonly) NSString *appID;
-
-@end
-
-@implementation FIRDeviceCheckAPIService
-
-- (instancetype)initWithAPIService:(id<FIRAppCheckAPIServiceProtocol>)APIService
-                         projectID:(NSString *)projectID
-                             appID:(NSString *)appID {
-  self = [super init];
-  if (self) {
-    _APIService = APIService;
-    _projectID = projectID;
-    _appID = appID;
-  }
-  return self;
-}
-
-#pragma mark - Public API
-
-- (FBLPromise<FIRAppCheckToken *> *)appCheckTokenWithDeviceToken:(NSData *)deviceToken {
-  NSString *URLString =
-      [NSString stringWithFormat:@"%@/projects/%@/apps/%@:exchangeDeviceCheckToken",
-                                 self.APIService.baseURL, self.projectID, self.appID];
-  NSURL *URL = [NSURL URLWithString:URLString];
-
-  return [self HTTPBodyWithDeviceToken:deviceToken]
-      .then(^FBLPromise<GULURLSessionDataResponse *> *(NSData *HTTPBody) {
-        return [self.APIService sendRequestWithURL:URL
-                                        HTTPMethod:@"POST"
-                                              body:HTTPBody
-                                 additionalHeaders:@{kContentTypeKey : kJSONContentType}];
-      })
-      .then(^id _Nullable(GULURLSessionDataResponse *_Nullable response) {
-        return [self.APIService appCheckTokenWithAPIResponse:response];
-      });
-}
-
-- (FBLPromise<NSData *> *)HTTPBodyWithDeviceToken:(NSData *)deviceToken {
-  if (deviceToken.length <= 0) {
-    FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
-    [rejectedPromise reject:[FIRAppCheckErrorUtil
-                                errorWithFailureReason:@"DeviceCheck token must not be empty."]];
-    return rejectedPromise;
-  }
-
-  return [FBLPromise onQueue:[self backgroundQueue]
-                          do:^id _Nullable {
-                            NSString *base64EncodedToken =
-                                [deviceToken base64EncodedStringWithOptions:0];
-
-                            NSError *encodingError;
-                            NSData *payloadJSON = [NSJSONSerialization
-                                dataWithJSONObject:@{kDeviceTokenField : base64EncodedToken}
-                                           options:0
-                                             error:&encodingError];
-
-                            if (payloadJSON != nil) {
-                              return payloadJSON;
-                            } else {
-                              return [FIRAppCheckErrorUtil JSONSerializationError:encodingError];
-                            }
-                          }];
-}
-
-- (dispatch_queue_t)backgroundQueue {
-  return dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
-}
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 33
FirebaseAppCheck/Sources/DeviceCheckProvider/DCDevice+FIRDeviceCheckTokenGenerator.h

@@ -1,33 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckAvailability.h"
-
-#if FIR_DEVICE_CHECK_SUPPORTED_TARGETS
-
-#import <DeviceCheck/DeviceCheck.h>
-
-#import "FirebaseAppCheck/Sources/DeviceCheckProvider/FIRDeviceCheckTokenGenerator.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface DCDevice (FIRDeviceCheckTokenGenerator) <FIRDeviceCheckTokenGenerator>
-
-@end
-
-NS_ASSUME_NONNULL_END
-
-#endif  // FIR_DEVICE_CHECK_SUPPORTED_TARGETS

+ 0 - 25
FirebaseAppCheck/Sources/DeviceCheckProvider/DCDevice+FIRDeviceCheckTokenGenerator.m

@@ -1,25 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "FirebaseAppCheck/Sources/DeviceCheckProvider/DCDevice+FIRDeviceCheckTokenGenerator.h"
-
-#if FIR_DEVICE_CHECK_SUPPORTED_TARGETS
-
-@implementation DCDevice (FIRDeviceCheckTokenGenerator)
-
-@end
-
-#endif  // FIR_DEVICE_CHECK_SUPPORTED_TARGETS

+ 22 - 94
FirebaseAppCheck/Sources/DeviceCheckProvider/FIRDeviceCheckProvider.m

@@ -18,62 +18,36 @@
 
 #if FIR_DEVICE_CHECK_SUPPORTED_TARGETS
 
-#import <Foundation/Foundation.h>
-
-#if __has_include(<FBLPromises/FBLPromises.h>)
-#import <FBLPromises/FBLPromises.h>
-#else
-#import "FBLPromises.h"
-#endif
-
 #import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRDeviceCheckProvider.h"
 
-#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h"
-#import "FirebaseAppCheck/Sources/Core/Backoff/FIRAppCheckBackoffWrapper.h"
-#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
+#import <AppCheck/AppCheck.h>
+
+#import "FirebaseAppCheck/Sources/Core/FIRApp+AppCheck.h"
 #import "FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h"
+#import "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.h"
 #import "FirebaseAppCheck/Sources/Core/FIRAppCheckValidator.h"
-#import "FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h"
-#import "FirebaseAppCheck/Sources/DeviceCheckProvider/DCDevice+FIRDeviceCheckTokenGenerator.h"
-#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckToken.h"
+#import "FirebaseAppCheck/Sources/Core/FIRHeartbeatLogger+AppCheck.h"
 
 #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
 
 NS_ASSUME_NONNULL_BEGIN
 
 @interface FIRDeviceCheckProvider ()
-@property(nonatomic, readonly) id<FIRDeviceCheckAPIServiceProtocol> APIService;
-@property(nonatomic, readonly) id<FIRDeviceCheckTokenGenerator> deviceTokenGenerator;
-@property(nonatomic, readonly) id<FIRAppCheckBackoffWrapperProtocol> backoffWrapper;
 
-- (instancetype)initWithAPIService:(id<FIRDeviceCheckAPIServiceProtocol>)APIService
-              deviceTokenGenerator:(id<FIRDeviceCheckTokenGenerator>)deviceTokenGenerator
-                    backoffWrapper:(id<FIRAppCheckBackoffWrapperProtocol>)backoffWrapper
-    NS_DESIGNATED_INITIALIZER;
+@property(nonatomic, readonly) GACDeviceCheckProvider *deviceCheckProvider;
 
 @end
 
 @implementation FIRDeviceCheckProvider
 
-- (instancetype)initWithAPIService:(id<FIRDeviceCheckAPIServiceProtocol>)APIService
-              deviceTokenGenerator:(id<FIRDeviceCheckTokenGenerator>)deviceTokenGenerator
-                    backoffWrapper:(id<FIRAppCheckBackoffWrapperProtocol>)backoffWrapper {
+- (instancetype)initWithDeviceCheckProvider:(GACDeviceCheckProvider *)deviceCheckProvider {
   self = [super init];
   if (self) {
-    _APIService = APIService;
-    _deviceTokenGenerator = deviceTokenGenerator;
-    _backoffWrapper = backoffWrapper;
+    _deviceCheckProvider = deviceCheckProvider;
   }
   return self;
 }
 
-- (instancetype)initWithAPIService:(id<FIRDeviceCheckAPIServiceProtocol>)APIService {
-  FIRAppCheckBackoffWrapper *backoffWrapper = [[FIRAppCheckBackoffWrapper alloc] init];
-  return [self initWithAPIService:APIService
-             deviceTokenGenerator:[DCDevice currentDevice]
-                   backoffWrapper:backoffWrapper];
-}
-
 - (nullable instancetype)initWithApp:(FIRApp *)app {
   NSArray<NSString *> *missingOptionsFields =
       [FIRAppCheckValidator tokenExchangeMissingFieldsInOptions:app.options];
@@ -86,74 +60,28 @@ NS_ASSUME_NONNULL_BEGIN
     return nil;
   }
 
-  NSURLSession *URLSession = [NSURLSession
-      sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
-
-  FIRAppCheckAPIService *APIService =
-      [[FIRAppCheckAPIService alloc] initWithURLSession:URLSession
+  GACDeviceCheckProvider *deviceCheckProvider =
+      [[GACDeviceCheckProvider alloc] initWithStorageID:app.name
+                                           resourceName:app.resourceName
                                                  APIKey:app.options.APIKey
-                                                  appID:app.options.googleAppID
-                                        heartbeatLogger:app.heartbeatLogger];
+                                           requestHooks:@[ [app.heartbeatLogger requestHook] ]];
 
-  FIRDeviceCheckAPIService *deviceCheckAPIService =
-      [[FIRDeviceCheckAPIService alloc] initWithAPIService:APIService
-                                                 projectID:app.options.projectID
-                                                     appID:app.options.googleAppID];
-
-  return [self initWithAPIService:deviceCheckAPIService];
+  return [self initWithDeviceCheckProvider:deviceCheckProvider];
 }
 
 #pragma mark - FIRAppCheckProvider
 
 - (void)getTokenWithCompletion:(void (^)(FIRAppCheckToken *_Nullable token,
                                          NSError *_Nullable error))handler {
-  [self.backoffWrapper
-      applyBackoffToOperation:^FBLPromise *_Nonnull {
-        return [self getTokenPromise];
-      }
-                 errorHandler:[self.backoffWrapper defaultAppCheckProviderErrorHandler]]
-      // Call the handler with either token or error.
-      .then(^id(FIRAppCheckToken *appCheckToken) {
-        handler(appCheckToken, nil);
-        return nil;
-      })
-      .catch(^void(NSError *error) {
-        handler(nil, error);
-      });
-}
-
-- (FBLPromise<FIRAppCheckToken *> *)getTokenPromise {
-  // Get DeviceCheck token
-  return [self deviceToken]
-      // Exchange DeviceCheck token for FAC token.
-      .then(^FBLPromise<FIRAppCheckToken *> *(NSData *deviceToken) {
-        return [self.APIService appCheckTokenWithDeviceToken:deviceToken];
-      });
-}
-
-#pragma mark - DeviceCheck
-
-- (FBLPromise<NSData *> *)deviceToken {
-  return [self isDeviceCheckSupported].then(^FBLPromise<NSData *> *(NSNull *ignored) {
-    return [FBLPromise
-        wrapObjectOrErrorCompletion:^(FBLPromiseObjectOrErrorCompletion _Nonnull handler) {
-          [self.deviceTokenGenerator generateTokenWithCompletionHandler:handler];
-        }];
-  });
-}
-
-#pragma mark - Helpers
-
-/// Returns a resolved promise if DeviceCheck is supported and a rejected promise if it is not.
-- (FBLPromise<NSNull *> *)isDeviceCheckSupported {
-  if (self.deviceTokenGenerator.isSupported) {
-    return [FBLPromise resolvedWith:[NSNull null]];
-  } else {
-    NSError *error = [FIRAppCheckErrorUtil unsupportedAttestationProvider:@"DeviceCheckProvider"];
-    FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
-    [rejectedPromise reject:error];
-    return rejectedPromise;
-  }
+  [self.deviceCheckProvider getTokenWithCompletion:^(GACAppCheckToken *_Nullable internalToken,
+                                                     NSError *_Nullable error) {
+    if (error) {
+      handler(nil, error);
+      return;
+    }
+
+    handler([[FIRAppCheckToken alloc] initWithInternalToken:internalToken], nil);
+  }];
 }
 
 @end

+ 0 - 30
FirebaseAppCheck/Sources/DeviceCheckProvider/FIRDeviceCheckTokenGenerator.h

@@ -1,30 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@protocol FIRDeviceCheckTokenGenerator <NSObject>
-
-@property(getter=isSupported, readonly) BOOL supported;
-
-- (void)generateTokenWithCompletionHandler:(void (^)(NSData* _Nullable token,
-                                                     NSError* _Nullable error))completion;
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 7
FirebaseAppCheck/Tests/Fixture/AppAttestAttestationResponseSuccess.json

@@ -1,7 +0,0 @@
-{
-  "artifact" : "dmFsaWQgRmlyZWJhc2UgYXBwIGF0dGVzdCBhcnRpZmFjdA==",
-  "appCheckToken" : {
-    "token": "valid_app_check_token",
-    "ttl": "1800s"
-  }
-}

+ 0 - 3
FirebaseAppCheck/Tests/Fixture/AppAttestResponseMissingChallenge.json

@@ -1,3 +0,0 @@
-{
-    "ttl": "300s"
-}

+ 0 - 4
FirebaseAppCheck/Tests/Fixture/AppAttestResponseSuccess.json

@@ -1,4 +0,0 @@
-{
-    "challenge": "cmFuZG9tX2NoYWxsZW5nZQ==",
-    "ttl": "300s"
-}

+ 0 - 3
FirebaseAppCheck/Tests/Fixture/DeviceCheckResponseMissingTimeToLive.json

@@ -1,3 +0,0 @@
-{
-    "token": "valid_app_check_token"
-}

+ 0 - 3
FirebaseAppCheck/Tests/Fixture/DeviceCheckResponseMissingToken.json

@@ -1,3 +0,0 @@
-{
-  "timeToLive": "3600s"
-}

+ 0 - 4
FirebaseAppCheck/Tests/Fixture/FACTokenExchangeResponseSuccess.json

@@ -1,4 +0,0 @@
-{
-  "token": "valid_app_check_token",
-  "ttl": "1800s"
-}

+ 55 - 0
FirebaseAppCheck/Tests/Integration/AppCheckE2ETests.swift

@@ -0,0 +1,55 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import XCTest
+
+import FirebaseAppCheck
+import FirebaseCore
+
+final class AppCheckE2ETests: XCTestCase {
+  // TODO(andrewheard): Add integration tests that exercise the public API.
+
+  let appName = "test_app_name"
+  var app: FirebaseApp!
+
+  override func setUp() {
+    let options = FirebaseOptions(googleAppID: "1:123456789:ios:abc123", gcmSenderID: "123456789")
+    options.projectID = "test_project_id"
+    options.apiKey = "test_api_key"
+    FirebaseApp.configure(name: appName, options: options)
+
+    app = FirebaseApp.app(name: appName)
+  }
+
+  override func tearDown() {
+    let semaphore = DispatchSemaphore(value: 0)
+    app.delete { _ in
+      semaphore.signal()
+    }
+    semaphore.wait()
+  }
+
+  func testInitAppCheck() throws {
+    AppCheck.setAppCheckProviderFactory(AppCheckDebugProviderFactory())
+    let appCheck = AppCheck.appCheck(app: app)
+
+    XCTAssertNotNil(appCheck)
+  }
+
+  func testInitAppCheckDebugProvider() throws {
+    let debugProvider = AppCheckDebugProvider(app: app)
+
+    XCTAssertNotNil(debugProvider)
+  }
+}

+ 0 - 101
FirebaseAppCheck/Tests/Integration/FIRDeviceCheckAPIServiceE2ETests.m

@@ -1,101 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <TargetConditionals.h>
-
-// Tests that use the Keychain require a host app and Swift Package Manager
-// does not support adding a host app to test targets.
-#if !SWIFT_PACKAGE
-
-// Skip keychain tests on Catalyst and macOS. Tests are skipped because they
-// involve interactions with the keychain that require a provisioning profile.
-// See go/firebase-macos-keychain-popups for more details.
-#if !TARGET_OS_MACCATALYST && !TARGET_OS_OSX
-
-#import <XCTest/XCTest.h>
-
-#import "FBLPromise+Testing.h"
-
-#import <FirebaseCore/FirebaseCore.h>
-
-#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h"
-#import "FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h"
-#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckToken.h"
-
-#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
-
-@interface FIRDeviceCheckAPIServiceE2ETests : XCTestCase
-@property(nonatomic) FIRDeviceCheckAPIService *deviceCheckAPIService;
-@property(nonatomic) FIRAppCheckAPIService *APIService;
-@property(nonatomic) NSURLSession *URLSession;
-@end
-
-// TODO(ncooke3): Fix these tests up and get them running on CI.
-
-@implementation FIRDeviceCheckAPIServiceE2ETests
-
-- (void)setUp {
-  self.URLSession = [NSURLSession
-      sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
-  FIROptions *options = [self firebaseTestOptions];
-  FIRHeartbeatLogger *heartbeatLogger =
-      [[FIRHeartbeatLogger alloc] initWithAppID:options.googleAppID];
-
-  self.APIService = [[FIRAppCheckAPIService alloc] initWithURLSession:self.URLSession
-                                                               APIKey:options.APIKey
-                                                                appID:options.googleAppID
-                                                      heartbeatLogger:heartbeatLogger];
-  self.deviceCheckAPIService =
-      [[FIRDeviceCheckAPIService alloc] initWithAPIService:self.APIService
-                                                 projectID:options.projectID
-                                                     appID:options.googleAppID];
-}
-
-- (void)tearDown {
-  self.deviceCheckAPIService = nil;
-  self.APIService = nil;
-  self.URLSession = nil;
-}
-
-// TODO: Re-enable the test once secret with "GoogleService-Info.plist" is configured.
-- (void)temporaryDisabled_testAppCheckTokenSuccess {
-  __auto_type appCheckPromise =
-      [self.deviceCheckAPIService appCheckTokenWithDeviceToken:[NSData data]];
-
-  XCTAssert(FBLWaitForPromisesWithTimeout(20));
-
-  XCTAssertNil(appCheckPromise.error);
-  XCTAssertNotNil(appCheckPromise.value);
-
-  XCTAssertNotNil(appCheckPromise.value.token);
-  XCTAssertNotNil(appCheckPromise.value.expirationDate);
-}
-
-#pragma mark - Helpers
-
-- (FIROptions *)firebaseTestOptions {
-  NSString *plistPath =
-      [[NSBundle bundleForClass:[self class]] pathForResource:@"GoogleService-Info"
-                                                       ofType:@"plist"];
-  FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:plistPath];
-  return options;
-}
-
-@end
-
-#endif  // !TARGET_OS_MACCATALYST && !TARGET_OS_OSX
-
-#endif  // !SWIFT_PACKAGE

+ 0 - 388
FirebaseAppCheck/Tests/Unit/Core/FIRAppCheckAPIServiceTests.m

@@ -1,388 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <XCTest/XCTest.h>
-
-#import <OCMock/OCMock.h>
-#import "FBLPromise+Testing.h"
-
-@import FirebaseCoreInternal;
-
-#import <GoogleUtilities/GULURLSessionDataResponse.h>
-#import <GoogleUtilities/NSURLSession+GULPromises.h>
-
-#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h"
-#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
-#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckErrors.h"
-#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckToken.h"
-
-#import "FirebaseAppCheck/Tests/Unit/Utils/FIRFixtureLoader.h"
-#import "SharedTestUtilities/Date/FIRDateTestUtils.h"
-#import "SharedTestUtilities/URLSession/FIRURLSessionOCMockStub.h"
-
-#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
-
-#pragma mark - Fakes
-
-/// A fake heartbeat logger used for dependency injection during testing.
-@interface FIRHeartbeatLoggerFake : NSObject <FIRHeartbeatLoggerProtocol>
-@property(nonatomic, copy, nullable) FIRHeartbeatsPayload * (^onFlushHeartbeatsIntoPayloadHandler)
-    (void);
-@property(nonatomic, copy, nullable) FIRDailyHeartbeatCode (^onHeartbeatCodeForTodayHandler)(void);
-@end
-
-@implementation FIRHeartbeatLoggerFake
-
-- (nonnull FIRHeartbeatsPayload *)flushHeartbeatsIntoPayload {
-  if (self.onFlushHeartbeatsIntoPayloadHandler) {
-    return self.onFlushHeartbeatsIntoPayloadHandler();
-  } else {
-    return nil;
-  }
-}
-
-- (FIRDailyHeartbeatCode)heartbeatCodeForToday {
-  // This API should not be used by the below tests because the AppCheck SDK
-  // uses only the V2 heartbeat API (`flushHeartbeatsIntoPayload`) for getting
-  // heartbeats.
-  [self doesNotRecognizeSelector:_cmd];
-  return FIRDailyHeartbeatCodeNone;
-}
-
-- (void)log {
-  // This API should not be used by the below tests because the AppCheck SDK
-  // does not log heartbeats in it's networking context.
-  [self doesNotRecognizeSelector:_cmd];
-}
-
-@end
-
-#pragma mark - FIRAppCheckAPIServiceTests
-
-@interface FIRAppCheckAPIServiceTests : XCTestCase
-
-@property(nonatomic) FIRAppCheckAPIService *APIService;
-
-@property(nonatomic) id mockURLSession;
-
-@property(nonatomic) NSString *APIKey;
-@property(nonatomic) NSString *appID;
-
-@property(nonatomic) FIRHeartbeatLoggerFake *heartbeatLoggerFake;
-
-@end
-
-@implementation FIRAppCheckAPIServiceTests
-
-- (void)setUp {
-  [super setUp];
-
-  self.APIKey = @"api_key";
-  self.appID = @"app_id";
-
-  self.mockURLSession = OCMStrictClassMock([NSURLSession class]);
-
-  self.heartbeatLoggerFake = [[FIRHeartbeatLoggerFake alloc] init];
-  self.APIService = [[FIRAppCheckAPIService alloc] initWithURLSession:self.mockURLSession
-                                                               APIKey:self.APIKey
-                                                                appID:self.appID
-                                                      heartbeatLogger:self.heartbeatLoggerFake];
-}
-
-- (void)tearDown {
-  [super tearDown];
-
-  self.APIService = nil;
-  [self.mockURLSession stopMocking];
-  self.mockURLSession = nil;
-}
-
-- (void)testDataRequestSuccessWhenNoHeartbeatsNeedSending {
-  // Given
-  FIRHeartbeatsPayload *emptyHeartbeatsPayload =
-      [FIRHeartbeatLoggingTestUtils emptyHeartbeatsPayload];
-  // When
-  self.heartbeatLoggerFake.onFlushHeartbeatsIntoPayloadHandler = ^FIRHeartbeatsPayload * {
-    return emptyHeartbeatsPayload;
-  };
-  // Then
-  [self assertDataRequestSuccessWhenSendingHeartbeatsPayload:emptyHeartbeatsPayload];
-}
-
-- (void)testDataRequestSuccessWhenHeartbeatsNeedSending {
-  // Given
-  FIRHeartbeatsPayload *nonEmptyHeartbeatsPayload =
-      [FIRHeartbeatLoggingTestUtils nonEmptyHeartbeatsPayload];
-  // When
-  self.heartbeatLoggerFake.onFlushHeartbeatsIntoPayloadHandler = ^FIRHeartbeatsPayload * {
-    return nonEmptyHeartbeatsPayload;
-  };
-  // Then
-  [self assertDataRequestSuccessWhenSendingHeartbeatsPayload:nonEmptyHeartbeatsPayload];
-}
-
-- (void)testDataRequestNetworkError {
-  NSURL *URL = [NSURL URLWithString:@"https://some.url.com"];
-  NSDictionary *additionalHeaders = @{@"header1" : @"value1"};
-  NSData *requestBody = [@"Request body" dataUsingEncoding:NSUTF8StringEncoding];
-
-  // 1. Stub URL session.
-  NSError *networkError = [NSError errorWithDomain:self.name code:-1 userInfo:nil];
-
-  [self stubURLSessionDataTaskPromiseWithResponse:nil
-                                             body:nil
-                                            error:networkError
-                                   URLSessionMock:self.mockURLSession
-                           requestValidationBlock:nil];
-
-  // 2. Send request.
-  __auto_type requestPromise = [self.APIService sendRequestWithURL:URL
-                                                        HTTPMethod:@"POST"
-                                                              body:requestBody
-                                                 additionalHeaders:additionalHeaders];
-
-  // 3. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(requestPromise.isRejected);
-  XCTAssertNotNil(requestPromise.error);
-  XCTAssertEqualObjects(requestPromise.error.domain, FIRAppCheckErrorDomain);
-  XCTAssertEqual(requestPromise.error.code, FIRAppCheckErrorCodeServerUnreachable);
-  XCTAssertEqualObjects(requestPromise.error.userInfo[NSUnderlyingErrorKey], networkError);
-
-  OCMVerifyAll(self.mockURLSession);
-}
-
-- (void)testDataRequestNot2xxHTTPStatusCode {
-  NSURL *URL = [NSURL URLWithString:@"https://some.url.com"];
-  NSData *requestBody = [@"Request body" dataUsingEncoding:NSUTF8StringEncoding];
-  NSString *responseBodyString = @"Token verification failed.";
-
-  NSData *HTTPResponseBody = [responseBodyString dataUsingEncoding:NSUTF8StringEncoding];
-  NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:300];
-  [self stubURLSessionDataTaskPromiseWithResponse:HTTPResponse
-                                             body:HTTPResponseBody
-                                            error:nil
-                                   URLSessionMock:self.mockURLSession
-                           requestValidationBlock:nil];
-
-  // 2. Send request.
-  __auto_type requestPromise = [self.APIService sendRequestWithURL:URL
-                                                        HTTPMethod:@"POST"
-                                                              body:requestBody
-                                                 additionalHeaders:nil];
-
-  // 3. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(requestPromise.isRejected);
-  XCTAssertNil(requestPromise.value);
-
-  XCTAssertNotNil(requestPromise.error);
-  XCTAssertEqualObjects(requestPromise.error.domain, FIRAppCheckErrorDomain);
-  XCTAssertEqual(requestPromise.error.code, FIRAppCheckErrorCodeUnknown);
-
-  // Expect response body and HTTP status code to be included in the error.
-  NSString *failureReason = requestPromise.error.userInfo[NSLocalizedFailureReasonErrorKey];
-  XCTAssertNotNil(failureReason);
-  XCTAssertTrue([failureReason containsString:@"300"]);
-  XCTAssertTrue([failureReason containsString:responseBodyString]);
-
-  OCMVerifyAll(self.mockURLSession);
-}
-
-#pragma mark - Token Exchange API response
-
-- (void)testAppCheckTokenWithAPIResponseValidResponse {
-  // 1. Prepare input parameters.
-  NSData *responseBody =
-      [FIRFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"];
-  XCTAssertNotNil(responseBody);
-  NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
-  GULURLSessionDataResponse *APIResponse =
-      [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
-
-  // 2. Expected result.
-  NSString *expectedFACToken = @"valid_app_check_token";
-
-  // 3. Parse API response.
-  __auto_type tokenPromise = [self.APIService appCheckTokenWithAPIResponse:APIResponse];
-
-  // 4. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(tokenPromise.isFulfilled);
-  XCTAssertNil(tokenPromise.error);
-
-  XCTAssertEqualObjects(tokenPromise.value.token, expectedFACToken);
-  XCTAssertTrue([FIRDateTestUtils isDate:tokenPromise.value.expirationDate
-      approximatelyEqualCurrentPlusTimeInterval:1800
-                                      precision:10]);
-}
-
-- (void)testAppCheckTokenWithAPIResponseInvalidFormat {
-  // 1. Prepare input parameters.
-  NSString *responseBodyString = @"Token verification failed.";
-  NSData *responseBody = [responseBodyString dataUsingEncoding:NSUTF8StringEncoding];
-  NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
-  GULURLSessionDataResponse *APIResponse =
-      [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
-
-  // 2. Parse API response.
-  __auto_type tokenPromise = [self.APIService appCheckTokenWithAPIResponse:APIResponse];
-
-  // 3. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(tokenPromise.isRejected);
-  XCTAssertNil(tokenPromise.value);
-
-  XCTAssertNotNil(tokenPromise.error);
-  XCTAssertEqualObjects(tokenPromise.error.domain, FIRAppCheckErrorDomain);
-  XCTAssertEqual(tokenPromise.error.code, FIRAppCheckErrorCodeUnknown);
-
-  // Expect response body and HTTP status code to be included in the error.
-  NSString *failureReason = tokenPromise.error.userInfo[NSLocalizedFailureReasonErrorKey];
-  XCTAssertEqualObjects(failureReason, @"JSON serialization error.");
-}
-
-- (void)testAppCheckTokenResponseMissingFields {
-  [self assertMissingFieldErrorWithFixture:@"DeviceCheckResponseMissingToken.json"
-                              missingField:@"token"];
-  [self assertMissingFieldErrorWithFixture:@"DeviceCheckResponseMissingTimeToLive.json"
-                              missingField:@"ttl"];
-}
-
-- (void)assertMissingFieldErrorWithFixture:(NSString *)fixtureName
-                              missingField:(NSString *)fieldName {
-  // 1. Parse API response.
-  NSData *missingFiledBody = [FIRFixtureLoader loadFixtureNamed:fixtureName];
-  XCTAssertNotNil(missingFiledBody);
-
-  NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
-  GULURLSessionDataResponse *APIResponse =
-      [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:missingFiledBody];
-
-  // 2. Parse API response.
-  __auto_type tokenPromise = [self.APIService appCheckTokenWithAPIResponse:APIResponse];
-
-  // 3. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(tokenPromise.isRejected);
-  XCTAssertNil(tokenPromise.value);
-
-  XCTAssertNotNil(tokenPromise.error);
-  XCTAssertEqualObjects(tokenPromise.error.domain, FIRAppCheckErrorDomain);
-  XCTAssertEqual(tokenPromise.error.code, FIRAppCheckErrorCodeUnknown);
-
-  // Expect missing field name to be included in the error.
-  NSString *failureReason = tokenPromise.error.userInfo[NSLocalizedFailureReasonErrorKey];
-  NSString *fieldNameString = [NSString stringWithFormat:@"`%@`", fieldName];
-  XCTAssertTrue([failureReason containsString:fieldNameString],
-                @"Fixture `%@`: expected missing field %@ error not found", fixtureName,
-                fieldNameString);
-}
-
-#pragma mark - Helpers
-
-- (void)assertDataRequestSuccessWhenSendingHeartbeatsPayload:
-    (nullable FIRHeartbeatsPayload *)heartbeatsPayload {
-  NSURL *URL = [NSURL URLWithString:@"https://some.url.com"];
-  NSDictionary *additionalHeaders = @{@"header1" : @"value1"};
-  NSData *requestBody = [@"Request body" dataUsingEncoding:NSUTF8StringEncoding];
-
-  // 1. Stub URL session.
-  FIRRequestValidationBlock requestValidation = ^BOOL(NSURLRequest *request) {
-    XCTAssertEqualObjects(request.URL, URL);
-
-    NSMutableDictionary<NSString *, NSString *> *expectedHTTPHeaderFields = @{
-      @"X-Goog-Api-Key" : self.APIKey,
-      @"X-Ios-Bundle-Identifier" : [[NSBundle mainBundle] bundleIdentifier],
-      @"header1" : @"value1",
-    }
-                                                                                .mutableCopy;
-
-    NSString *_Nullable heartbeatHeaderValue =
-        FIRHeaderValueFromHeartbeatsPayload(heartbeatsPayload);
-    if (heartbeatHeaderValue) {
-      expectedHTTPHeaderFields[@"X-firebase-client"] = heartbeatHeaderValue;
-    }
-
-    XCTAssertEqualObjects(request.allHTTPHeaderFields, expectedHTTPHeaderFields);
-
-    XCTAssertEqualObjects(request.HTTPMethod, @"POST");
-    XCTAssertEqualObjects(request.HTTPBody, requestBody);
-
-    return YES;
-  };
-
-  NSData *HTTPResponseBody = [@"A response" dataUsingEncoding:NSUTF8StringEncoding];
-  NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
-  [self stubURLSessionDataTaskPromiseWithResponse:HTTPResponse
-                                             body:HTTPResponseBody
-                                            error:nil
-                                   URLSessionMock:self.mockURLSession
-                           requestValidationBlock:requestValidation];
-
-  // 2. Send request.
-  __auto_type requestPromise = [self.APIService sendRequestWithURL:URL
-                                                        HTTPMethod:@"POST"
-                                                              body:requestBody
-                                                 additionalHeaders:additionalHeaders];
-
-  // 3. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(requestPromise.isFulfilled);
-  XCTAssertNil(requestPromise.error);
-
-  XCTAssertEqualObjects(requestPromise.value.HTTPResponse, HTTPResponse);
-  XCTAssertEqualObjects(requestPromise.value.HTTPBody, HTTPResponseBody);
-
-  OCMVerifyAll(self.mockURLSession);
-}
-
-- (void)stubURLSessionDataTaskPromiseWithResponse:(NSHTTPURLResponse *)HTTPResponse
-                                             body:(NSData *)body
-                                            error:(NSError *)error
-                                   URLSessionMock:(id)URLSessionMock
-                           requestValidationBlock:
-                               (FIRRequestValidationBlock)requestValidationBlock {
-  // Validate request content.
-  FIRRequestValidationBlock nonOptionalRequestValidationBlock =
-      requestValidationBlock ?: ^BOOL(id request) {
-        return YES;
-      };
-
-  id URLRequestValidationArg = [OCMArg checkWithBlock:nonOptionalRequestValidationBlock];
-
-  // Result promise.
-  FBLPromise<GULURLSessionDataResponse *> *result = [FBLPromise pendingPromise];
-  if (error == nil) {
-    GULURLSessionDataResponse *response =
-        [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:body];
-    [result fulfill:response];
-  } else {
-    [result reject:error];
-  }
-
-  // Stub the method.
-  OCMExpect([URLSessionMock gul_dataTaskPromiseWithRequest:URLRequestValidationArg])
-      .andReturn(result);
-}
-
-@end

+ 0 - 392
FirebaseAppCheck/Tests/Unit/Core/FIRAppCheckBackoffWrapperTests.m

@@ -1,392 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <XCTest/XCTest.h>
-
-#import "FBLPromise+Testing.h"
-#if __has_include(<FBLPromises/FBLPromises.h>)
-#import <FBLPromises/FBLPromises.h>
-#else
-#import "FBLPromises.h"
-#endif
-
-#import "FirebaseAppCheck/Sources/Core/Backoff/FIRAppCheckBackoffWrapper.h"
-
-#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckHTTPError.h"
-
-@interface FIRAppCheckBackoffWrapperTests : XCTestCase
-
-@property(nonatomic, nullable) FIRAppCheckBackoffWrapper *backoffWrapper;
-
-@property(nonatomic) NSDate *currentDate;
-
-/// `NSObject` subclass for resolve the `self.operation` with in the case of success or `NSError`
-/// for a failure.
-@property(nonatomic) id operationResult;
-/// Operation to apply backoff to. It configure with the helper methods during tests.
-@property(nonatomic) FIRAppCheckBackoffOperationProvider operationProvider;
-/// Expectation to fulfill when operation is completed. It is configured with the `self.operation`
-/// in setup helpers.
-@property(nonatomic) XCTestExpectation *operationFinishExpectation;
-
-/// Test error handler that returns `self.errorHandlerResult` and fulfills
-/// `self.errorHandlerExpectation`.
-@property(nonatomic, copy) FIRAppCheckBackoffErrorHandler errorHandler;
-/// Expectation to fulfill when error handlers is executed.
-@property(nonatomic) XCTestExpectation *errorHandlerExpectation;
-
-@end
-
-@implementation FIRAppCheckBackoffWrapperTests
-
-- (void)setUp {
-  [super setUp];
-
-  __auto_type __weak weakSelf = self;
-  self.backoffWrapper = [[FIRAppCheckBackoffWrapper alloc] initWithDateProvider:^NSDate *_Nonnull {
-    return weakSelf.currentDate ?: [NSDate date];
-  }];
-}
-
-- (void)tearDown {
-  self.backoffWrapper = nil;
-  self.operationProvider = nil;
-
-  [super tearDown];
-}
-
-- (void)testBackoffFirstOperationAlwaysExecuted {
-  // 1. Set up operation success.
-  [self setUpOperationSuccess];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffTypeNone];
-  self.errorHandlerExpectation.inverted = YES;
-
-  // 2. Compose operation with backoff.
-  __auto_type operationWithBackoff =
-      [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                      errorHandler:self.errorHandler];
-
-  // 3. Wait for operation to complete and check.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  XCTAssertEqualObjects(operationWithBackoff.value, self.operationResult);
-}
-
-- (void)testBackoff1DayBackoffAfterFailure {
-  // 0. Set current date.
-  self.currentDate = [NSDate date];
-
-  // 1. Check initial failure.
-  // 1.1. Set up operation failure.
-  [self setUpOperationError];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffType1Day];
-
-  // 1.2. Compose operation with backoff.
-  __auto_type operationWithBackoff =
-      [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                      errorHandler:self.errorHandler];
-
-  // 1.3. Wait for operation to complete.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  // 1.4. Expect the promise to be rejected with the operation error.
-  XCTAssertEqualObjects(operationWithBackoff.error, self.operationResult);
-
-  // 2. Check backoff in 12 hours.
-  // 2.1. Set up another operation.
-  [self setUpOperationError];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffType1Day];
-
-  // Don't expect operation to be called.
-  self.operationFinishExpectation.inverted = YES;
-  // Don't expect error handler to be called.
-  self.errorHandlerExpectation.inverted = YES;
-
-  // 2.2. Move current date.
-  self.currentDate = [self.currentDate dateByAddingTimeInterval:12 * 60 * 60];
-
-  // 2.3. Compose operation with backoff.
-  operationWithBackoff = [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                                         errorHandler:self.errorHandler];
-
-  // 2.4. Wait for operation to complete.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  // 2.5. Expect the promise to be rejected with a backoff error.
-  XCTAssertTrue(operationWithBackoff.isRejected);
-  XCTAssertTrue([self isBackoffError:operationWithBackoff.error]);
-
-  // 3. Check backoff one minute before allowing retry.
-  // 3.1. Set up another operation.
-  [self setUpOperationError];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffType1Day];
-
-  // Don't expect operation to be called.
-  self.operationFinishExpectation.inverted = YES;
-  // Don't expect error handler to be called.
-  self.errorHandlerExpectation.inverted = YES;
-
-  // 3.2. Move current date.
-  self.currentDate = [self.currentDate dateByAddingTimeInterval:11 * 60 * 60 + 59 * 60];
-
-  // 3.3. Compose operation with backoff.
-  operationWithBackoff = [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                                         errorHandler:self.errorHandler];
-
-  // 3.4. Wait for operation to complete.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  // 3.5. Expect the promise to be rejected with a backoff error.
-  XCTAssertTrue(operationWithBackoff.isRejected);
-  XCTAssertTrue([self isBackoffError:operationWithBackoff.error]);
-
-  // 4. Check backoff one minute after allowing retry.
-  // 4.1. Set up another operation.
-  [self setUpOperationError];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffType1Day];
-
-  // 4.2. Move current date.
-  self.currentDate = [self.currentDate dateByAddingTimeInterval:12 * 60 * 60 + 1 * 60];
-
-  // 4.3. Compose operation with backoff.
-  operationWithBackoff = [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                                         errorHandler:self.errorHandler];
-
-  // 4.4. Wait for operation to complete and check failure.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  // 4.5. Expect the promise to be rejected with the operation error.
-  XCTAssertEqualObjects(operationWithBackoff.error, self.operationResult);
-}
-
-#pragma mark - Exponential backoff
-
-- (void)testExponentialBackoff {
-  // 0. Set current date.
-  self.currentDate = [NSDate date];
-
-  // 1. Check initial failure.
-  // 1.1. Set up operation failure.
-  [self setUpOperationError];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffTypeExponential];
-
-  // 1.2. Compose operation with backoff.
-  __auto_type operationWithBackoff =
-      [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                      errorHandler:self.errorHandler];
-
-  // 1.4. Wait for operation to complete.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  // 1.5. Expect the promise to be rejected with the operation error.
-  XCTAssertEqualObjects(operationWithBackoff.error, self.operationResult);
-
-  // 2. Check exponential backoff.
-  NSUInteger numberOfAttempts = 20;
-  NSTimeInterval maximumBackoff = 4 * 60 * 60;  // 4 hours.
-  // The maximum of original backoff interval that can be added.
-  double maxJitterPortion = 0.5;  // Backoff is up to 50% longer.
-
-  for (NSUInteger attempt = 0; attempt < numberOfAttempts; attempt++) {
-    NSTimeInterval expectedMinBackoff = MIN(pow(2, attempt), maximumBackoff);
-    NSTimeInterval expectedMaxBackoff =
-        MIN(expectedMinBackoff * (1 + maxJitterPortion), maximumBackoff);
-
-    [self assertBackoffIntervalIsAtLeast:expectedMinBackoff andAtMost:expectedMaxBackoff];
-  }
-
-  // 3. Test recovery after success.
-  // 3.1. Set time after max backoff.
-  self.currentDate = [self.currentDate dateByAddingTimeInterval:maximumBackoff];
-
-  // 3.2. Set up operation success.
-  [self setUpOperationSuccess];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffTypeNone];
-  self.errorHandlerExpectation.inverted = YES;
-
-  // 3.3. Compose operation with backoff.
-  operationWithBackoff = [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                                         errorHandler:self.errorHandler];
-
-  // 3.4. Wait for operation to complete.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  // 3.5. Expect the promise to be rejected with the operation error.
-  XCTAssertEqualObjects(operationWithBackoff.value, self.operationResult);
-
-  // 3.6. Set up operation failure.
-  // We expect an operation to be executed with no backoff after a success.
-  [self setUpOperationError];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffTypeExponential];
-
-  // 3.7. Compose operation with backoff.
-  operationWithBackoff = [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                                         errorHandler:self.errorHandler];
-
-  // 3.8. Wait for operation to complete.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  // 3.9. Expect the promise to be rejected with the operation error.
-  XCTAssertEqualObjects(operationWithBackoff.error, self.operationResult);
-}
-
-#pragma mark - Error handling
-
-- (void)testDefaultAppCheckProviderErrorHandler {
-  __auto_type errorHandler = [self.backoffWrapper defaultAppCheckProviderErrorHandler];
-
-  NSError *nonHTTPError = [NSError errorWithDomain:self.name code:1 userInfo:nil];
-  XCTAssertEqual(errorHandler(nonHTTPError), FIRAppCheckBackoffTypeNone);
-
-  FIRAppCheckHTTPError *HTTP400Error = [self httpErrorWithStatusCode:400];
-  XCTAssertEqual(errorHandler(HTTP400Error), FIRAppCheckBackoffType1Day);
-
-  FIRAppCheckHTTPError *HTTP403Error = [self httpErrorWithStatusCode:403];
-  XCTAssertEqual(errorHandler(HTTP403Error), FIRAppCheckBackoffTypeExponential);
-
-  FIRAppCheckHTTPError *HTTP404Error = [self httpErrorWithStatusCode:404];
-  XCTAssertEqual(errorHandler(HTTP404Error), FIRAppCheckBackoffType1Day);
-
-  FIRAppCheckHTTPError *HTTP429Error = [self httpErrorWithStatusCode:429];
-  XCTAssertEqual(errorHandler(HTTP429Error), FIRAppCheckBackoffTypeExponential);
-
-  FIRAppCheckHTTPError *HTTP503Error = [self httpErrorWithStatusCode:503];
-  XCTAssertEqual(errorHandler(HTTP503Error), FIRAppCheckBackoffTypeExponential);
-
-  // Test all other codes from 400 to 599.
-  for (NSInteger statusCode = 400; statusCode < 600; statusCode++) {
-    if (statusCode == 400 || statusCode == 404) {
-      // Skip status codes with non-exponential backoff.
-      continue;
-    }
-
-    FIRAppCheckHTTPError *HTTPError = [self httpErrorWithStatusCode:statusCode];
-    XCTAssertEqual(errorHandler(HTTPError), FIRAppCheckBackoffTypeExponential);
-  }
-}
-
-#pragma mark - Helpers
-
-- (void)setUpErrorHandlerWithBackoffType:(FIRAppCheckBackoffType)backoffType {
-  __auto_type __weak weakSelf = self;
-  self.errorHandlerExpectation = [self expectationWithDescription:@"Error handler"];
-  self.errorHandler = ^FIRAppCheckBackoffType(NSError *_Nonnull error) {
-    [weakSelf.errorHandlerExpectation fulfill];
-    return backoffType;
-  };
-}
-
-- (void)setUpOperationSuccess {
-  self.operationFinishExpectation = [self expectationWithDescription:@"Operation performed"];
-  self.operationResult = [[NSObject alloc] init];
-  __auto_type __weak weakSelf = self;
-  self.operationProvider = ^FBLPromise *() {
-    return [FBLPromise do:^id(void) {
-      [weakSelf.operationFinishExpectation fulfill];
-      return weakSelf.operationResult;
-    }];
-  };
-}
-
-- (void)setUpOperationError {
-  self.operationFinishExpectation = [self expectationWithDescription:@"Operation performed"];
-  self.operationResult = [NSError errorWithDomain:self.name code:-1 userInfo:nil];
-  __auto_type __weak weakSelf = self;
-  self.operationProvider = ^FBLPromise *() {
-    return [FBLPromise do:^id(void) {
-      [weakSelf.operationFinishExpectation fulfill];
-      return weakSelf.operationResult;
-    }];
-  };
-}
-
-- (BOOL)isBackoffError:(NSError *)error {
-  return [error.localizedDescription containsString:@"Too many attempts. Underlying error:"];
-}
-
-- (FIRAppCheckHTTPError *)httpErrorWithStatusCode:(NSInteger)statusCode {
-  NSHTTPURLResponse *httpResponse =
-      [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://localhost"]
-                                  statusCode:statusCode
-                                 HTTPVersion:nil
-                                headerFields:nil];
-  FIRAppCheckHTTPError *error = [[FIRAppCheckHTTPError alloc] initWithHTTPResponse:httpResponse
-                                                                              data:nil];
-  return error;
-}
-
-// Asserts that the backoff interval is within the provided range.
-// Assumes that `self.currentDate` contains the last failure date.
-// Sets `self.currentDate` to the date when the most recent retry happened.
-- (void)assertBackoffIntervalIsAtLeast:(NSTimeInterval)minBackoff
-                             andAtMost:(NSTimeInterval)maxBackoff {
-  NSDate *lastFailureDate = self.currentDate;
-
-  // 1. Test backoff before min interval.
-  // 1.1 Move the date 0.5 sec before the minimum backoff date.
-  self.currentDate = [lastFailureDate dateByAddingTimeInterval:minBackoff - 0.5];
-
-  // 1.2 Set up operation failure.
-  [self setUpOperationError];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffTypeExponential];
-
-  // 1.3 Don't expect operation to be executed.
-  self.operationFinishExpectation.inverted = YES;
-  self.errorHandlerExpectation.inverted = YES;
-
-  // 1.4 Compose operation with backoff.
-  __auto_type operationWithBackoff =
-      [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                      errorHandler:self.errorHandler];
-
-  // 1.5 Wait for operation to complete.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  // 1.6 Expect the promise to be rejected with a backoff error.
-  XCTAssertTrue(operationWithBackoff.isRejected);
-  XCTAssertTrue([self isBackoffError:operationWithBackoff.error]);
-
-  // 2. Test backoff after max interval.
-  // 2.1 Move the date 0.5 sec before the minimum backoff date.
-  self.currentDate = [lastFailureDate dateByAddingTimeInterval:maxBackoff + 0.5];
-
-  // 2.2. Set up operation failure and expect it to be completed.
-  [self setUpOperationError];
-  [self setUpErrorHandlerWithBackoffType:FIRAppCheckBackoffTypeExponential];
-
-  // 2.3 Compose operation with backoff.
-  operationWithBackoff = [self.backoffWrapper applyBackoffToOperation:self.operationProvider
-                                                         errorHandler:self.errorHandler];
-
-  // 2.4 Wait for operation to complete.
-  [self waitForExpectationsWithTimeout:0.5 handler:NULL];
-  XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
-
-  // 2.5 Expect the promise to be rejected with a backoff error.
-  XCTAssertTrue(operationWithBackoff.isRejected);
-  XCTAssertFalse([self isBackoffError:operationWithBackoff.error]);
-}
-
-@end

+ 0 - 41
FirebaseAppCheck/Tests/Unit/Core/FIRAppCheckCryptoUtilsTests.m

@@ -1,41 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <XCTest/XCTest.h>
-
-#import "FirebaseAppCheck/Sources/Core/Utils/FIRAppCheckCryptoUtils.h"
-
-@interface FIRAppCheckCryptoUtilsTests : XCTestCase
-
-@end
-
-@implementation FIRAppCheckCryptoUtilsTests
-
-- (void)testSHA256HashFromData {
-  NSData *dataToHash = [@"some data to hash" dataUsingEncoding:NSUTF8StringEncoding];
-
-  NSData *hashData = [FIRAppCheckCryptoUtils sha256HashFromData:dataToHash];
-
-  // Convert to a base64 encoded string to compare.
-  NSString *base64EncodedHashString = [hashData base64EncodedStringWithOptions:0];
-
-  // Base64 encoded hash of UTF8 encoded string "some data to hash".
-  NSString *expectedHashString = @"ai2iCUOTHpg0/BLP5btHu9muQ0iaMHJpYrV29OOZPlA=";
-
-  XCTAssertEqualObjects(base64EncodedHashString, expectedHashString);
-}
-
-@end

+ 0 - 250
FirebaseAppCheck/Tests/Unit/DeviceCheckProvider/FIRDeviceCheckAPIServiceTests.m

@@ -1,250 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <XCTest/XCTest.h>
-
-#import <OCMock/OCMock.h>
-#import "FBLPromise+Testing.h"
-
-#import <FirebaseAppCheck/FirebaseAppCheck.h>
-#import <GoogleUtilities/GULURLSessionDataResponse.h>
-
-#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h"
-#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
-#import "FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h"
-#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckErrors.h"
-
-#import "FirebaseAppCheck/Tests/Unit/Utils/FIRFixtureLoader.h"
-#import "SharedTestUtilities/URLSession/FIRURLSessionOCMockStub.h"
-
-#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
-
-typedef BOOL (^FIRRequestValidationBlock)(NSURLRequest *request);
-
-@interface FIRDeviceCheckAPIServiceTests : XCTestCase
-@property(nonatomic) FIRDeviceCheckAPIService *APIService;
-
-@property(nonatomic) id mockAPIService;
-
-@property(nonatomic) NSString *projectID;
-@property(nonatomic) NSString *appID;
-
-@end
-
-@implementation FIRDeviceCheckAPIServiceTests
-
-- (void)setUp {
-  [super setUp];
-
-  self.projectID = @"project_id";
-  self.appID = @"app_id";
-
-  self.mockAPIService = OCMProtocolMock(@protocol(FIRAppCheckAPIServiceProtocol));
-  OCMStub([self.mockAPIService baseURL]).andReturn(@"https://test.appcheck.url.com/alpha");
-
-  self.APIService = [[FIRDeviceCheckAPIService alloc] initWithAPIService:self.mockAPIService
-                                                               projectID:self.projectID
-                                                                   appID:self.appID];
-}
-
-- (void)tearDown {
-  self.APIService = nil;
-  [self.mockAPIService stopMocking];
-  self.mockAPIService = nil;
-
-  [super tearDown];
-}
-
-- (void)testAppCheckTokenSuccess {
-  NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
-  FIRAppCheckToken *expectedResult = [[FIRAppCheckToken alloc] initWithToken:@"app_check_token"
-                                                              expirationDate:[NSDate date]];
-
-  // 1. Stub API service.
-  // 1.1 Stub send request.
-  NSString *expectedRequestURL =
-      [NSString stringWithFormat:@"%@%@", [self.mockAPIService baseURL],
-                                 @"/projects/project_id/apps/app_id:exchangeDeviceCheckToken"];
-  id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) {
-    XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL);
-    return YES;
-  }];
-
-  id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
-
-  NSData *responseBody =
-      [FIRFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"];
-  XCTAssertNotNil(responseBody);
-
-  NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
-  GULURLSessionDataResponse *APIResponse =
-      [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
-
-  OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg
-                                         HTTPMethod:@"POST"
-                                               body:HTTPBodyValidationArg
-                                  additionalHeaders:@{@"Content-Type" : @"application/json"}])
-      .andReturn([FBLPromise resolvedWith:APIResponse]);
-
-  // 1.2. Stub response parsing.
-  OCMExpect([self.mockAPIService appCheckTokenWithAPIResponse:APIResponse])
-      .andReturn([FBLPromise resolvedWith:expectedResult]);
-
-  // 2. Send request.
-  __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
-
-  // 3. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(tokenPromise.isFulfilled);
-  XCTAssertNil(tokenPromise.error);
-
-  XCTAssertEqualObjects(tokenPromise.value.token, expectedResult.token);
-  XCTAssertEqualObjects(tokenPromise.value.expirationDate, expectedResult.expirationDate);
-
-  OCMVerifyAll(self.mockAPIService);
-}
-
-- (void)testAppCheckTokenResponseParsingError {
-  NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
-  NSError *parsingError = [NSError errorWithDomain:@"testAppCheckTokenResponseParsingError"
-                                              code:-1
-                                          userInfo:nil];
-
-  // 1. Stub API service.
-  // 1.1 Stub send request.
-  NSString *expectedRequestURL =
-      [NSString stringWithFormat:@"%@%@", [self.mockAPIService baseURL],
-                                 @"/projects/project_id/apps/app_id:exchangeDeviceCheckToken"];
-  id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) {
-    XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL);
-    return YES;
-  }];
-
-  id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
-
-  NSData *responseBody =
-      [FIRFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"];
-  XCTAssertNotNil(responseBody);
-
-  NSHTTPURLResponse *HTTPResponse = [FIRURLSessionOCMockStub HTTPResponseWithCode:200];
-  GULURLSessionDataResponse *APIResponse =
-      [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody];
-
-  OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg
-                                         HTTPMethod:@"POST"
-                                               body:HTTPBodyValidationArg
-                                  additionalHeaders:@{@"Content-Type" : @"application/json"}])
-      .andReturn([FBLPromise resolvedWith:APIResponse]);
-
-  // 1.2. Stub response parsing.
-  FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
-  [rejectedPromise reject:parsingError];
-  OCMExpect([self.mockAPIService appCheckTokenWithAPIResponse:APIResponse])
-      .andReturn(rejectedPromise);
-
-  // 2. Send request.
-  __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
-
-  // 3. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(tokenPromise.isRejected);
-  XCTAssertEqualObjects(tokenPromise.error, parsingError);
-  XCTAssertNil(tokenPromise.value);
-
-  OCMVerifyAll(self.mockAPIService);
-}
-
-- (void)testAppCheckTokenNetworkError {
-  NSData *deviceTokenData = [@"device_token" dataUsingEncoding:NSUTF8StringEncoding];
-  NSError *APIError = [NSError errorWithDomain:@"testAppCheckTokenNetworkError"
-                                          code:-1
-                                      userInfo:nil];
-
-  // 1. Stub API service.
-  FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
-  [rejectedPromise reject:APIError];
-
-  id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDeviceToken:deviceTokenData];
-  OCMExpect([self.mockAPIService sendRequestWithURL:[OCMArg any]
-                                         HTTPMethod:@"POST"
-                                               body:HTTPBodyValidationArg
-                                  additionalHeaders:@{@"Content-Type" : @"application/json"}])
-      .andReturn(rejectedPromise);
-
-  // 2. Send request.
-  __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
-
-  // 3. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(tokenPromise.isRejected);
-  XCTAssertNil(tokenPromise.value);
-  XCTAssertEqualObjects(tokenPromise.error, APIError);
-
-  OCMVerifyAll(self.mockAPIService);
-}
-
-- (void)testAppCheckTokenEmptyDeviceToken {
-  NSData *deviceTokenData = [NSData data];
-
-  // 1. Stub API service.
-  OCMReject([self.mockAPIService sendRequestWithURL:[OCMArg any]
-                                         HTTPMethod:[OCMArg any]
-                                               body:[OCMArg any]
-                                  additionalHeaders:[OCMArg any]]);
-
-  // 2. Send request.
-  __auto_type tokenPromise = [self.APIService appCheckTokenWithDeviceToken:deviceTokenData];
-
-  // 3. Verify.
-  XCTAssert(FBLWaitForPromisesWithTimeout(1));
-
-  XCTAssertTrue(tokenPromise.isRejected);
-  XCTAssertNil(tokenPromise.value);
-
-  XCTAssertNotNil(tokenPromise.error);
-  XCTAssertEqualObjects(tokenPromise.error.domain, FIRAppCheckErrorDomain);
-  XCTAssertEqual(tokenPromise.error.code, FIRAppCheckErrorCodeUnknown);
-
-  // Expect response body and HTTP status code to be included in the error.
-  NSString *failureReason = tokenPromise.error.userInfo[NSLocalizedFailureReasonErrorKey];
-  XCTAssertEqualObjects(failureReason, @"DeviceCheck token must not be empty.");
-
-  OCMVerifyAll(self.mockAPIService);
-}
-
-#pragma mark - Helpers
-
-- (id)HTTPBodyValidationArgWithDeviceToken:(NSData *)deviceToken {
-  return [OCMArg checkWithBlock:^BOOL(NSData *body) {
-    NSDictionary<NSString *, id> *decodedData = [NSJSONSerialization JSONObjectWithData:body
-                                                                                options:0
-                                                                                  error:nil];
-    XCTAssert([decodedData isKindOfClass:[NSDictionary class]]);
-
-    NSString *base64EncodedDeviceToken = decodedData[@"device_token"];
-    XCTAssertNotNil(base64EncodedDeviceToken);
-
-    NSData *decodedToken = [[NSData alloc] initWithBase64EncodedString:base64EncodedDeviceToken
-                                                               options:0];
-    XCTAssertEqualObjects(decodedToken, deviceToken);
-    return YES;
-  }];
-}
-
-@end

+ 57 - 244
FirebaseAppCheck/Tests/Unit/DeviceCheckProvider/FIRDeviceCheckProviderTests.m

@@ -21,32 +21,31 @@
 
 #import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
 #import "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.h"
-#import "FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h"
-#import "FirebaseAppCheck/Sources/DeviceCheckProvider/FIRDeviceCheckTokenGenerator.h"
 #import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRDeviceCheckProvider.h"
 
 #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
 
-#import "SharedTestUtilities/AppCheckBackoffWrapperFake/FIRAppCheckBackoffWrapperFake.h"
-
 #if FIR_DEVICE_CHECK_SUPPORTED_TARGETS
 
+static NSString *const kAppName = @"test_app_name";
+static NSString *const kAppID = @"test_app_id";
+static NSString *const kAPIKey = @"test_api_key";
+static NSString *const kProjectID = @"test_project_id";
+static NSString *const kProjectNumber = @"123456789";
+
 FIR_DEVICE_CHECK_PROVIDER_AVAILABILITY
 @interface FIRDeviceCheckProvider (Tests)
 
-- (instancetype)initWithAPIService:(id<FIRDeviceCheckAPIServiceProtocol>)APIService
-              deviceTokenGenerator:(id<FIRDeviceCheckTokenGenerator>)deviceTokenGenerator
-                    backoffWrapper:(id<FIRAppCheckBackoffWrapperProtocol>)backoffWrapper;
+- (instancetype)initWithDeviceCheckProvider:(GACDeviceCheckProvider *)deviceCheckProvider;
 
 @end
 
 FIR_DEVICE_CHECK_PROVIDER_AVAILABILITY
 @interface FIRDeviceCheckProviderTests : XCTestCase
 
+@property(nonatomic, copy) NSString *resourceName;
+@property(nonatomic) id deviceCheckProviderMock;
 @property(nonatomic) FIRDeviceCheckProvider *provider;
-@property(nonatomic) id fakeAPIService;
-@property(nonatomic) id fakeTokenGenerator;
-@property(nonatomic) FIRAppCheckBackoffWrapperFake *fakeBackoffWrapper;
 
 @end
 
@@ -55,274 +54,88 @@ FIR_DEVICE_CHECK_PROVIDER_AVAILABILITY
 - (void)setUp {
   [super setUp];
 
-  self.fakeAPIService = OCMProtocolMock(@protocol(FIRDeviceCheckAPIServiceProtocol));
-  self.fakeTokenGenerator = OCMProtocolMock(@protocol(FIRDeviceCheckTokenGenerator));
-
-  self.fakeBackoffWrapper = [[FIRAppCheckBackoffWrapperFake alloc] init];
-  // Don't backoff by default.
-  self.fakeBackoffWrapper.isNextOperationAllowed = YES;
-
-  self.provider = [[FIRDeviceCheckProvider alloc] initWithAPIService:self.fakeAPIService
-                                                deviceTokenGenerator:self.fakeTokenGenerator
-                                                      backoffWrapper:self.fakeBackoffWrapper];
+  self.resourceName = [NSString stringWithFormat:@"projects/%@/apps/%@", kProjectID, kAppID];
+  self.deviceCheckProviderMock = OCMStrictClassMock([GACDeviceCheckProvider class]);
+  self.provider =
+      [[FIRDeviceCheckProvider alloc] initWithDeviceCheckProvider:self.deviceCheckProviderMock];
 }
 
 - (void)tearDown {
   self.provider = nil;
-  self.fakeAPIService = nil;
-  self.fakeTokenGenerator = nil;
-  self.fakeBackoffWrapper = nil;
+  [self.deviceCheckProviderMock stopMocking];
+  self.deviceCheckProviderMock = nil;
 }
 
 - (void)testInitWithValidApp {
-  FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"app_id" GCMSenderID:@"sender_id"];
-  options.APIKey = @"api_key";
-  options.projectID = @"project_id";
-  FIRApp *app = [[FIRApp alloc] initInstanceWithName:@"testInitWithValidApp" options:options];
+  FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kAppID GCMSenderID:kProjectNumber];
+  options.APIKey = kAPIKey;
+  options.projectID = kProjectID;
+  FIRApp *app = [[FIRApp alloc] initInstanceWithName:kAppName options:options];
+
+  OCMExpect([self.deviceCheckProviderMock alloc]).andReturn(self.deviceCheckProviderMock);
+  OCMExpect([self.deviceCheckProviderMock initWithStorageID:kAppName
+                                               resourceName:self.resourceName
+                                                     APIKey:kAPIKey
+                                               requestHooks:OCMOCK_ANY])
+      .andReturn(self.deviceCheckProviderMock);
 
   XCTAssertNotNil([[FIRDeviceCheckProvider alloc] initWithApp:app]);
+
+  OCMVerifyAll(self.deviceCheckProviderMock);
 }
 
 - (void)testInitWithIncompleteApp {
-  FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"app_id" GCMSenderID:@"sender_id"];
+  FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kAppID GCMSenderID:kProjectNumber];
+  options.projectID = kProjectID;
+  FIRApp *missingAPIKeyApp = [[FIRApp alloc] initInstanceWithName:kAppName options:options];
 
-  options.projectID = @"project_id";
-  FIRApp *missingAPIKeyApp = [[FIRApp alloc] initInstanceWithName:@"testInitWithValidApp"
-                                                          options:options];
   XCTAssertNil([[FIRDeviceCheckProvider alloc] initWithApp:missingAPIKeyApp]);
 
   options.projectID = nil;
-  options.APIKey = @"api_key";
-  FIRApp *missingProjectIDApp = [[FIRApp alloc] initInstanceWithName:@"testInitWithValidApp"
-                                                             options:options];
+  options.APIKey = kAPIKey;
+  FIRApp *missingProjectIDApp = [[FIRApp alloc] initInstanceWithName:kAppName options:options];
   XCTAssertNil([[FIRDeviceCheckProvider alloc] initWithApp:missingProjectIDApp]);
+
+  OCMVerifyAll(self.deviceCheckProviderMock);
 }
 
 - (void)testGetTokenSuccess {
-  // 1. Expect FIRDeviceCheckTokenGenerator.isSupported.
-  OCMExpect([self.fakeTokenGenerator isSupported]).andReturn(YES);
-
-  // 2. Expect device token to be generated.
-  NSData *deviceToken = [NSData data];
-  id generateTokenArg = [OCMArg invokeBlockWithArgs:deviceToken, [NSNull null], nil];
-  OCMExpect([self.fakeTokenGenerator generateTokenWithCompletionHandler:generateTokenArg]);
-
-  // 3. Expect FAA token to be requested.
-  FIRAppCheckToken *validToken = [[FIRAppCheckToken alloc] initWithToken:@"valid_token"
-                                                          expirationDate:[NSDate distantFuture]
-                                                          receivedAtDate:[NSDate date]];
-  OCMExpect([self.fakeAPIService appCheckTokenWithDeviceToken:deviceToken])
-      .andReturn([FBLPromise resolvedWith:validToken]);
-
-  // 4. Expect backoff wrapper to be used.
-  self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"];
-
-  // 5. Call getToken and validate the result.
-  XCTestExpectation *completionExpectation =
-      [self expectationWithDescription:@"completionExpectation"];
+  // 1. Stub internal DeviceCheck provider.
+  GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"valid_token"
+                                                                  expirationDate:[NSDate date]
+                                                                  receivedAtDate:[NSDate date]];
+  OCMExpect([self.deviceCheckProviderMock
+      getTokenWithCompletion:([OCMArg
+                                 invokeBlockWithArgs:validInternalToken, [NSNull null], nil])]);
+
+  // 2. Validate get token.
   [self.provider
       getTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
-        [completionExpectation fulfill];
-        XCTAssertEqualObjects(token.token, validToken.token);
-        XCTAssertEqualObjects(token.expirationDate, validToken.expirationDate);
-        XCTAssertEqualObjects(token.receivedAtDate, validToken.receivedAtDate);
+        XCTAssertEqualObjects(token.token, validInternalToken.token);
+        XCTAssertEqualObjects(token.expirationDate, validInternalToken.expirationDate);
+        XCTAssertEqualObjects(token.receivedAtDate, validInternalToken.receivedAtDate);
         XCTAssertNil(error);
       }];
 
-  [self waitForExpectations:@[ self.fakeBackoffWrapper.backoffExpectation, completionExpectation ]
-                    timeout:0.5
-               enforceOrder:YES];
-
-  // 6. Verify.
-  XCTAssertNil(self.fakeBackoffWrapper.operationError);
-  FIRAppCheckToken *wrapperResult =
-      [self.fakeBackoffWrapper.operationResult isKindOfClass:[FIRAppCheckToken class]]
-          ? self.fakeBackoffWrapper.operationResult
-          : nil;
-  XCTAssertEqualObjects(wrapperResult.token, validToken.token);
-
-  OCMVerifyAll(self.fakeAPIService);
-  OCMVerifyAll(self.fakeTokenGenerator);
+  // 3. Verify mock DeviceCheck provider.
+  OCMVerifyAll(self.deviceCheckProviderMock);
 }
 
-- (void)testGetTokenWhenDeviceCheckIsNotSupported {
-  NSError *expectedError =
-      [FIRAppCheckErrorUtil unsupportedAttestationProvider:@"DeviceCheckProvider"];
-
-  // 0.1. Expect backoff wrapper to be used.
-  self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"];
-
-  // 0.2. Expect default error handler to be used.
-  XCTestExpectation *errorHandlerExpectation = [self expectationWithDescription:@"Error handler"];
-  self.fakeBackoffWrapper.defaultErrorHandler = ^FIRAppCheckBackoffType(NSError *_Nonnull error) {
-    XCTAssertEqualObjects(error, expectedError);
-    [errorHandlerExpectation fulfill];
-    return FIRAppCheckBackoffType1Day;
-  };
+- (void)testGetTokenAPIError {
+  // 1. Stub internal DeviceCheck provider.
+  NSError *expectedError = [NSError errorWithDomain:@"testGetTokenAPIError" code:-1 userInfo:nil];
+  OCMExpect([self.deviceCheckProviderMock
+      getTokenWithCompletion:([OCMArg invokeBlockWithArgs:[NSNull null], expectedError, nil])]);
 
-  // 1. Expect FIRDeviceCheckTokenGenerator.isSupported.
-  OCMExpect([self.fakeTokenGenerator isSupported]).andReturn(NO);
-
-  // 2. Don't expect DeviceCheck token to be generated or FAA token to be requested.
-  OCMReject([self.fakeTokenGenerator generateTokenWithCompletionHandler:OCMOCK_ANY]);
-  OCMReject([self.fakeAPIService appCheckTokenWithDeviceToken:OCMOCK_ANY]);
-
-  // 3. Call getToken and validate the result.
-  XCTestExpectation *completionExpectation =
-      [self expectationWithDescription:@"completionExpectation"];
+  // 2. Validate get token.
   [self.provider
       getTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
-        [completionExpectation fulfill];
         XCTAssertNil(token);
         XCTAssertEqualObjects(error, expectedError);
       }];
 
-  [self waitForExpectations:@[
-    self.fakeBackoffWrapper.backoffExpectation, errorHandlerExpectation, completionExpectation
-  ]
-                    timeout:0.5
-               enforceOrder:YES];
-
-  // 4. Verify.
-  OCMVerifyAll(self.fakeAPIService);
-  OCMVerifyAll(self.fakeTokenGenerator);
-
-  XCTAssertEqualObjects(self.fakeBackoffWrapper.operationError, expectedError);
-  XCTAssertNil(self.fakeBackoffWrapper.operationResult);
-}
-
-- (void)testGetTokenWhenDeviceTokenFails {
-  NSError *deviceTokenError = [NSError errorWithDomain:@"FIRDeviceCheckProviderTests"
-                                                  code:-1
-                                              userInfo:nil];
-
-  // 0.1. Expect backoff wrapper to be used.
-  self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"];
-
-  // 0.2. Expect default error handler to be used.
-  XCTestExpectation *errorHandlerExpectation = [self expectationWithDescription:@"Error handler"];
-  self.fakeBackoffWrapper.defaultErrorHandler = ^FIRAppCheckBackoffType(NSError *_Nonnull error) {
-    XCTAssertEqualObjects(error, deviceTokenError);
-    [errorHandlerExpectation fulfill];
-    return FIRAppCheckBackoffType1Day;
-  };
-
-  // 1. Expect FIRDeviceCheckTokenGenerator.isSupported.
-  OCMExpect([self.fakeTokenGenerator isSupported]).andReturn(YES);
-
-  // 2. Expect device token to be generated.
-  id generateTokenArg = [OCMArg invokeBlockWithArgs:[NSNull null], deviceTokenError, nil];
-  OCMExpect([self.fakeTokenGenerator generateTokenWithCompletionHandler:generateTokenArg]);
-
-  // 3. Don't expect FAA token to be requested.
-  OCMReject([self.fakeAPIService appCheckTokenWithDeviceToken:[OCMArg any]]);
-
-  // 4. Call getToken and validate the result.
-  XCTestExpectation *completionExpectation =
-      [self expectationWithDescription:@"completionExpectation"];
-  [self.provider
-      getTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
-        [completionExpectation fulfill];
-        XCTAssertNil(token);
-        XCTAssertEqualObjects(error, deviceTokenError);
-      }];
-
-  [self waitForExpectations:@[
-    self.fakeBackoffWrapper.backoffExpectation, errorHandlerExpectation, completionExpectation
-  ]
-                    timeout:0.5
-               enforceOrder:YES];
-
-  // 5. Verify.
-  OCMVerifyAll(self.fakeAPIService);
-  OCMVerifyAll(self.fakeTokenGenerator);
-
-  XCTAssertEqualObjects(self.fakeBackoffWrapper.operationError, deviceTokenError);
-  XCTAssertNil(self.fakeBackoffWrapper.operationResult);
-}
-
-- (void)testGetTokenWhenAPIServiceFails {
-  NSError *APIServiceError = [NSError errorWithDomain:@"FIRDeviceCheckProviderTests"
-                                                 code:-1
-                                             userInfo:nil];
-
-  // 0.1. Expect backoff wrapper to be used.
-  self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"];
-
-  // 0.2. Expect default error handler to be used.
-  XCTestExpectation *errorHandlerExpectation = [self expectationWithDescription:@"Error handler"];
-  self.fakeBackoffWrapper.defaultErrorHandler = ^FIRAppCheckBackoffType(NSError *_Nonnull error) {
-    XCTAssertEqualObjects(error, APIServiceError);
-    [errorHandlerExpectation fulfill];
-    return FIRAppCheckBackoffType1Day;
-  };
-
-  // 1. Expect FIRDeviceCheckTokenGenerator.isSupported.
-  OCMExpect([self.fakeTokenGenerator isSupported]).andReturn(YES);
-
-  // 2. Expect device token to be generated.
-  NSData *deviceToken = [NSData data];
-  id generateTokenArg = [OCMArg invokeBlockWithArgs:deviceToken, [NSNull null], nil];
-  OCMExpect([self.fakeTokenGenerator generateTokenWithCompletionHandler:generateTokenArg]);
-
-  // 3. Expect FAA token to be requested.
-  FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
-  [rejectedPromise reject:APIServiceError];
-  OCMExpect([self.fakeAPIService appCheckTokenWithDeviceToken:deviceToken])
-      .andReturn(rejectedPromise);
-
-  // 4. Call getToken and validate the result.
-  XCTestExpectation *completionExpectation =
-      [self expectationWithDescription:@"completionExpectation"];
-  [self.provider
-      getTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
-        [completionExpectation fulfill];
-        XCTAssertNil(token);
-        XCTAssertEqualObjects(error, APIServiceError);
-      }];
-
-  [self waitForExpectations:@[
-    self.fakeBackoffWrapper.backoffExpectation, errorHandlerExpectation, completionExpectation
-  ]
-                    timeout:0.5
-               enforceOrder:YES];
-
-  // 5. Verify.
-  OCMVerifyAll(self.fakeAPIService);
-  OCMVerifyAll(self.fakeTokenGenerator);
-
-  XCTAssertEqualObjects(self.fakeBackoffWrapper.operationError, APIServiceError);
-  XCTAssertNil(self.fakeBackoffWrapper.operationResult);
-}
-
-#pragma mark - Backoff tests
-
-- (void)testGetTokenBackoff {
-  // 1. Configure backoff.
-  self.fakeBackoffWrapper.isNextOperationAllowed = NO;
-  self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"];
-
-  // 2. Don't expect any operations.
-  OCMReject([self.fakeAPIService appCheckTokenWithDeviceToken:[OCMArg any]]);
-  OCMReject([self.fakeTokenGenerator generateTokenWithCompletionHandler:OCMOCK_ANY]);
-
-  // 3. Call getToken and validate the result.
-  XCTestExpectation *completionExpectation =
-      [self expectationWithDescription:@"completionExpectation"];
-  [self.provider
-      getTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
-        [completionExpectation fulfill];
-        XCTAssertNil(token);
-        XCTAssertEqualObjects(error, self.fakeBackoffWrapper.backoffError);
-      }];
-
-  [self waitForExpectations:@[ self.fakeBackoffWrapper.backoffExpectation, completionExpectation ]
-                    timeout:0.5
-               enforceOrder:YES];
-
-  // 4. Verify.
-  OCMVerifyAll(self.fakeAPIService);
-  OCMVerifyAll(self.fakeTokenGenerator);
+  // 3. Verify mock DeviceCheck provider.
+  OCMVerifyAll(self.deviceCheckProviderMock);
 }
 
 @end

+ 9 - 11
Package.swift

@@ -1258,26 +1258,25 @@ let package = Package(
         "SharedTestUtilities",
         .product(name: "OCMock", package: "ocmock"),
       ],
-      path: "FirebaseAppCheck/Tests",
+      path: "FirebaseAppCheck/Tests/Unit",
       exclude: [
         // Swift tests are in the target `FirebaseAppCheckUnitSwift` since mixed language targets
         // are not supported (as of Xcode 14.3).
-        "Unit/Swift",
-      ],
-      resources: [
-        .process("Fixture"),
+        "Swift",
       ],
       cSettings: [
-        .headerSearchPath("../.."),
+        .headerSearchPath("../../.."),
       ]
     ),
     .testTarget(
       name: "FirebaseAppCheckUnitSwift",
       dependencies: ["FirebaseAppCheck"],
-      path: "FirebaseAppCheck/Tests/Unit/Swift",
-      cSettings: [
-        .headerSearchPath("../.."),
-      ]
+      path: "FirebaseAppCheck/Tests/Unit/Swift"
+    ),
+    .testTarget(
+      name: "FirebaseAppCheckIntegration",
+      dependencies: ["FirebaseAppCheck"],
+      path: "FirebaseAppCheck/Tests/Integration"
     ),
 
     // MARK: - App Check
@@ -1317,7 +1316,6 @@ let package = Package(
       dependencies: [
         "AppCheck",
         "SharedTestUtilities",
-        "HeartbeatLoggingTestUtils",
         .product(name: "OCMock", package: "ocmock"),
       ],
       path: "AppCheck/Tests",

+ 0 - 48
SharedTestUtilities/AppCheckBackoffWrapperFake/FIRAppCheckBackoffWrapperFake.h

@@ -1,48 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-#import <XCTest/XCTest.h>
-
-#import "FirebaseAppCheck/Sources/Core/Backoff/FIRAppCheckBackoffWrapper.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FIRAppCheckBackoffWrapperFake : NSObject <FIRAppCheckBackoffWrapperProtocol>
-
-/// If `YES` then the next operation passed to `[backoff:errorHandler:]` method will be performed.
-/// If `NO` then it will fail with a backoff error.
-@property(nonatomic) BOOL isNextOperationAllowed;
-
-/// Result of the last performed operation if it succeeded.
-@property(nonatomic, nullable, readonly) id operationResult;
-
-/// Error of the last performed operation if it failed.
-@property(nonatomic, nullable, readonly) NSError *operationError;
-
-/// Default error handler.
-@property(nonatomic, copy) FIRAppCheckBackoffErrorHandler defaultErrorHandler;
-
-/// Assign expectation to fulfill on  `[backoff:errorHandler:]` method call to this property.
-@property(nonatomic, nullable) XCTestExpectation *backoffExpectation;
-
-/// Error returned when retry is not allowed.
-@property(nonatomic, readonly) NSError *backoffError;
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 0 - 71
SharedTestUtilities/AppCheckBackoffWrapperFake/FIRAppCheckBackoffWrapperFake.m

@@ -1,71 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "SharedTestUtilities/AppCheckBackoffWrapperFake/FIRAppCheckBackoffWrapperFake.h"
-
-#if __has_include(<FBLPromises/FBLPromises.h>)
-#import <FBLPromises/FBLPromises.h>
-#else
-#import "FBLPromises.h"
-#endif
-
-NS_ASSUME_NONNULL_BEGIN
-
-@implementation FIRAppCheckBackoffWrapperFake
-
-- (FBLPromise *)applyBackoffToOperation:(FIRAppCheckBackoffOperationProvider)operationProvider
-                           errorHandler:(FIRAppCheckBackoffErrorHandler)errorHandler {
-  [self.backoffExpectation fulfill];
-
-  if (self.isNextOperationAllowed) {
-    return operationProvider()
-        .then(^id(id value) {
-          self->_operationResult = value;
-          self->_operationError = nil;
-          return value;
-        })
-        .recover(^id(NSError *error) {
-          self->_operationError = error;
-          self->_operationResult = nil;
-
-          errorHandler(error);
-
-          return error;
-        });
-  } else {
-    FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
-    [rejectedPromise reject:self.backoffError];
-    return rejectedPromise;
-  }
-}
-
-- (FIRAppCheckBackoffErrorHandler)defaultAppCheckProviderErrorHandler {
-  if (_defaultErrorHandler) {
-    return _defaultErrorHandler;
-  }
-
-  return ^FIRAppCheckBackoffType(NSError *error) {
-    return FIRAppCheckBackoffTypeNone;
-  };
-}
-
-- (NSError *)backoffError {
-  return [NSError errorWithDomain:@"FIRAppCheckBackoffWrapperFake.backoff" code:-1 userInfo:nil];
-}
-
-@end
-
-NS_ASSUME_NONNULL_END

+ 68 - 0
scripts/spm_test_schemes/FirebaseAppCheckIntegration.xcscheme

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1230"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseAppCheckIntegration"
+               BuildableName = "FirebaseAppCheckIntegration"
+               BlueprintName = "FirebaseAppCheckIntegration"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseAppCheckIntegration"
+               BuildableName = "FirebaseAppCheckIntegration"
+               BlueprintName = "FirebaseAppCheckIntegration"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>