From bdcb2463926e985dcd500b43566dc487bcfc6cfd Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Wed, 20 Sep 2023 14:02:51 -0400 Subject: [PATCH] Remove AppCheckCore --- AppCheckCore/CHANGELOG.md | 2 - .../AppCheckCoreInterop/AppCheckCoreInterop.h | 17 - .../GACAppCheckTokenInterop.h | 40 - AppCheckCore/Interop/dummy.m | 16 - .../API/GACAppAttestAPIService.h | 72 -- .../API/GACAppAttestAPIService.m | 264 ---- .../API/GACAppAttestAttestationResponse.h | 43 - .../API/GACAppAttestAttestationResponse.m | 96 -- .../DCAppAttestService+GACAppAttestService.h | 37 - .../DCAppAttestService+GACAppAttestService.m | 26 - .../Errors/GACAppAttestRejectionError.h | 27 - .../Errors/GACAppAttestRejectionError.m | 29 - .../AppAttestProvider/GACAppAttestProvider.m | 522 -------- .../GACAppAttestProviderState.h | 73 -- .../GACAppAttestProviderState.m | 57 - .../AppAttestProvider/GACAppAttestService.h | 42 - .../Storage/GACAppAttestArtifactStorage.h | 68 - .../Storage/GACAppAttestArtifactStorage.m | 117 -- .../Storage/GACAppAttestKeyIDStorage.h | 58 - .../Storage/GACAppAttestKeyIDStorage.m | 87 -- .../Storage/GACAppAttestStoredArtifact.h | 41 - .../Storage/GACAppAttestStoredArtifact.m | 74 -- .../Core/Backoff/GACAppCheckBackoffWrapper.h | 87 -- .../Core/Errors/GACAppCheckErrorUtil.h | 56 - .../Core/Errors/GACAppCheckErrorUtil.m | 146 --- .../Core/Errors/GACAppCheckHTTPError.h | 32 - .../Core/Errors/GACAppCheckHTTPError.m | 75 -- AppCheckCore/Sources/Core/GACAppCheck.m | 243 ---- .../Sources/Core/GACAppCheckLogger+Internal.h | 50 - AppCheckCore/Sources/Core/GACAppCheckLogger.m | 82 -- .../Sources/Core/GACAppCheckSettings.m | 31 - AppCheckCore/Sources/Core/GACAppCheckToken.m | 45 - .../Sources/Core/Storage/GACAppCheckStorage.h | 64 - .../Sources/Core/Storage/GACAppCheckStorage.m | 109 -- .../GACAppCheckStoredToken+GACAppCheckToken.h | 31 - .../GACAppCheckStoredToken+GACAppCheckToken.m | 35 - .../Core/Storage/GACAppCheckStoredToken.h | 37 - .../Core/Storage/GACAppCheckStoredToken.m | 62 - .../Core/TokenRefresh/GACAppCheckTimer.h | 41 - .../Core/TokenRefresh/GACAppCheckTimer.m | 91 -- .../GACAppCheckTokenRefreshResult.h | 63 - .../GACAppCheckTokenRefreshResult.m | 64 - .../TokenRefresh/GACAppCheckTokenRefresher.h | 69 - .../TokenRefresh/GACAppCheckTokenRefresher.m | 216 ---- .../Core/Utils/GACAppCheckCryptoUtils.h | 27 - .../API/GACAppCheckDebugProviderAPIService.h | 45 - .../API/GACAppCheckDebugProviderAPIService.m | 111 -- .../DebugProvider/GACAppCheckDebugProvider.m | 124 -- .../API/GACDeviceCheckAPIService.h | 44 - .../DCDevice+GACDeviceCheckTokenGenerator.h | 41 - .../DCDevice+GACDeviceCheckTokenGenerator.m | 31 - .../GACDeviceCheckProvider.m | 133 -- .../GACDeviceCheckTokenGenerator.h | 28 - .../Sources/Public/AppCheckCore/AppCheck.h | 32 - .../Public/AppCheckCore/AppCheckCore.h | 32 - .../AppCheckCore/GACAppAttestProvider.h | 81 -- .../Sources/Public/AppCheckCore/GACAppCheck.h | 77 -- .../AppCheckCore/GACAppCheckAvailability.h | 69 - .../AppCheckCore/GACAppCheckDebugProvider.h | 93 -- .../Public/AppCheckCore/GACAppCheckErrors.h | 56 - .../Public/AppCheckCore/GACAppCheckLogger.h | 44 - .../Public/AppCheckCore/GACAppCheckProvider.h | 41 - .../Public/AppCheckCore/GACAppCheckSettings.h | 33 - .../Public/AppCheckCore/GACAppCheckToken.h | 57 - .../AppCheckCore/GACAppCheckTokenDelegate.h | 33 - .../AppCheckCore/GACDeviceCheckProvider.h | 56 - .../GACAppAttestAPIServiceTests.m | 590 --------- .../GACAppAttestProviderTests.m | 1110 ----------------- .../GACAppAttestArtifactStorageTests.m | 302 ----- .../Storage/GACAppAttestKeyIDStorageTests.m | 160 --- .../Tests/Unit/Core/GACAppCheckStorageTests.m | 200 --- .../Unit/Core/GACAppCheckStoredTokenTests.m | 71 -- .../Tests/Unit/Core/GACAppCheckTests.m | 695 ----------- .../Tests/Unit/Core/GACAppCheckTimerTests.m | 74 -- .../Core/GACAppCheckTokenRefresherTests.m | 503 -------- .../GACAppCheckDebugProviderAPIServiceTests.m | 200 --- .../GACAppCheckDebugProviderTests.m | 181 --- .../GACDeviceCheckProviderTests.m | 275 ---- .../Tests/Unit/Swift/AppCheckAPITests.swift | 208 --- AppCheckCore/Tests/Unit/Utils/GACFakeTimer.h | 40 - AppCheckCore/Tests/Unit/Utils/GACFakeTimer.m | 39 - .../Tests/Unit/Utils/GACFixtureLoader.h | 25 - .../Tests/Unit/Utils/GACFixtureLoader.m | 54 - .../Tests/Utils/Date/GACDateTestUtils.h | 33 - .../Tests/Utils/Date/GACDateTestUtils.m | 30 - .../URLSession/GACURLSessionOCMockStub.m | 65 - 86 files changed, 9680 deletions(-) delete mode 100644 AppCheckCore/CHANGELOG.md delete mode 100644 AppCheckCore/Interop/Public/AppCheckCoreInterop/AppCheckCoreInterop.h delete mode 100644 AppCheckCore/Interop/Public/AppCheckCoreInterop/GACAppCheckTokenInterop.h delete mode 100644 AppCheckCore/Interop/dummy.m delete mode 100644 AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.h delete mode 100644 AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m delete mode 100644 AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h delete mode 100644 AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.m delete mode 100644 AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.h delete mode 100644 AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.m delete mode 100644 AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.h delete mode 100644 AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.m delete mode 100644 AppCheckCore/Sources/AppAttestProvider/GACAppAttestProvider.m delete mode 100644 AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.h delete mode 100644 AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.m delete mode 100644 AppCheckCore/Sources/AppAttestProvider/GACAppAttestService.h delete mode 100644 AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.h delete mode 100644 AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.m delete mode 100644 AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.h delete mode 100644 AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.m delete mode 100644 AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.h delete mode 100644 AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.m delete mode 100644 AppCheckCore/Sources/Core/Backoff/GACAppCheckBackoffWrapper.h delete mode 100644 AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h delete mode 100644 AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.m delete mode 100644 AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h delete mode 100644 AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.m delete mode 100644 AppCheckCore/Sources/Core/GACAppCheck.m delete mode 100644 AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h delete mode 100644 AppCheckCore/Sources/Core/GACAppCheckLogger.m delete mode 100644 AppCheckCore/Sources/Core/GACAppCheckSettings.m delete mode 100644 AppCheckCore/Sources/Core/GACAppCheckToken.m delete mode 100644 AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.h delete mode 100644 AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.m delete mode 100644 AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.h delete mode 100644 AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.m delete mode 100644 AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.h delete mode 100644 AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.m delete mode 100644 AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.h delete mode 100644 AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.m delete mode 100644 AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h delete mode 100644 AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.m delete mode 100644 AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.h delete mode 100644 AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.m delete mode 100644 AppCheckCore/Sources/Core/Utils/GACAppCheckCryptoUtils.h delete mode 100644 AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h delete mode 100644 AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.m delete mode 100644 AppCheckCore/Sources/DebugProvider/GACAppCheckDebugProvider.m delete mode 100644 AppCheckCore/Sources/DeviceCheckProvider/API/GACDeviceCheckAPIService.h delete mode 100644 AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.h delete mode 100644 AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.m delete mode 100644 AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckProvider.m delete mode 100644 AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckTokenGenerator.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/AppCheck.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/AppCheckCore.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppAttestProvider.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppCheck.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckAvailability.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckDebugProvider.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckLogger.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckProvider.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckTokenDelegate.h delete mode 100644 AppCheckCore/Sources/Public/AppCheckCore/GACDeviceCheckProvider.h delete mode 100644 AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m delete mode 100644 AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestProviderTests.m delete mode 100644 AppCheckCore/Tests/Unit/AppAttestProvider/Storage/GACAppAttestArtifactStorageTests.m delete mode 100644 AppCheckCore/Tests/Unit/AppAttestProvider/Storage/GACAppAttestKeyIDStorageTests.m delete mode 100644 AppCheckCore/Tests/Unit/Core/GACAppCheckStorageTests.m delete mode 100644 AppCheckCore/Tests/Unit/Core/GACAppCheckStoredTokenTests.m delete mode 100644 AppCheckCore/Tests/Unit/Core/GACAppCheckTests.m delete mode 100644 AppCheckCore/Tests/Unit/Core/GACAppCheckTimerTests.m delete mode 100644 AppCheckCore/Tests/Unit/Core/GACAppCheckTokenRefresherTests.m delete mode 100644 AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m delete mode 100644 AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderTests.m delete mode 100644 AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckProviderTests.m delete mode 100644 AppCheckCore/Tests/Unit/Swift/AppCheckAPITests.swift delete mode 100644 AppCheckCore/Tests/Unit/Utils/GACFakeTimer.h delete mode 100644 AppCheckCore/Tests/Unit/Utils/GACFakeTimer.m delete mode 100644 AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.h delete mode 100644 AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.m delete mode 100644 AppCheckCore/Tests/Utils/Date/GACDateTestUtils.h delete mode 100644 AppCheckCore/Tests/Utils/Date/GACDateTestUtils.m delete mode 100644 AppCheckCore/Tests/Utils/URLSession/GACURLSessionOCMockStub.m diff --git a/AppCheckCore/CHANGELOG.md b/AppCheckCore/CHANGELOG.md deleted file mode 100644 index 0a69820401ba..000000000000 --- a/AppCheckCore/CHANGELOG.md +++ /dev/null @@ -1,2 +0,0 @@ -# unreleased -- [added] Add generic (non-Firebase) App Check SDK. diff --git a/AppCheckCore/Interop/Public/AppCheckCoreInterop/AppCheckCoreInterop.h b/AppCheckCore/Interop/Public/AppCheckCoreInterop/AppCheckCoreInterop.h deleted file mode 100644 index bddf99ec845f..000000000000 --- a/AppCheckCore/Interop/Public/AppCheckCoreInterop/AppCheckCoreInterop.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2022 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 diff --git a/AppCheckCore/Interop/Public/AppCheckCoreInterop/GACAppCheckTokenInterop.h b/AppCheckCore/Interop/Public/AppCheckCoreInterop/GACAppCheckTokenInterop.h deleted file mode 100644 index dfa87093d2c2..000000000000 --- a/AppCheckCore/Interop/Public/AppCheckCoreInterop/GACAppCheckTokenInterop.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2022 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 - -#import "FirebaseAuth/Sources/Backend/FIRAuthRPCResponse.h" - -NS_ASSUME_NONNULL_BEGIN - -/** @class FIRVerifyPasswordResponse - @brief Represents the response from the getRecaptchaConfig endpoint. - */ -@interface FIRGetRecaptchaConfigResponse : NSObject - -/** @property recaptchaKey - @brief The recaptcha key of the project. - */ -@property(nonatomic, copy, nullable) NSString *recaptchaKey; - -/** @property enforcementState - @brief The enforcement state array. - */ -@property(nonatomic, nullable) NSArray *enforcementState; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Interop/dummy.m b/AppCheckCore/Interop/dummy.m deleted file mode 100644 index 229724c7f2c5..000000000000 --- a/AppCheckCore/Interop/dummy.m +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2022 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. - */ - diff --git a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.h b/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.h deleted file mode 100644 index a7bc3cb8444b..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.h +++ /dev/null @@ -1,72 +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 - -@class FBLPromise; -@class GACAppAttestAttestationResponse; -@class GACAppCheckToken; -@protocol GACAppCheckAPIServiceProtocol; - -NS_ASSUME_NONNULL_BEGIN - -/// Methods to send API requests required for App Attest based attestation sequence. -@protocol GACAppAttestAPIServiceProtocol - -/// Request a random challenge from server. -- (FBLPromise *)getRandomChallenge; - -/// Sends attestation data to the App Check backend for validation. -/// @param attestation The App Attest key attestation data obtained from the method -/// `-[DCAppAttestService attestKey:clientDataHash:completionHandler:]` using the random challenge -/// received from App Check backend. -/// @param keyID The key ID used to generate the attestation. -/// @param challenge The challenge used to generate the attestation. -/// @return A promise that is fulfilled with a response object with an encrypted attestation -/// artifact and an App Check token or rejected with an error. -- (FBLPromise *)attestKeyWithAttestation:(NSData *)attestation - keyID:(NSString *)keyID - challenge:(NSData *)challenge; - -/// Exchanges attestation data (artifact & assertion) and a challenge for a FAC token. -- (FBLPromise *)getAppCheckTokenWithArtifact:(NSData *)artifact - challenge:(NSData *)challenge - assertion:(NSData *)assertion; - -@end - -/// A default implementation of `GACAppAttestAPIServiceProtocol`. -@interface GACAppAttestAPIService : NSObject - -/// Default initializer. -/// -/// TODO(andrewheard): Remove or refactor the `limitedUse` parameter from this constructor when the -/// short-lived (limited-use) token feature is fully implemented. -/// -/// @param APIService An instance implementing `GACAppCheckAPIServiceProtocol` to be used to send -/// network requests to the App Check backend. -/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is -/// "projects/{project_id}/apps/{app_id}". -/// @param limitedUse If YES, forces a short-lived token with a 5 minute TTL. -- (instancetype)initWithAPIService:(id)APIService - resourceName:(NSString *)resourceName - limitedUse:(BOOL)limitedUse NS_DESIGNATED_INITIALIZER; - -- (instancetype)init NS_UNAVAILABLE; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m b/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m deleted file mode 100644 index 0c12f8dfccd6..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m +++ /dev/null @@ -1,264 +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 "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.h" - -#if __has_include() -#import -#else -#import "FBLPromises.h" -#endif - -#import "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h" -#import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" - -#import - -NS_ASSUME_NONNULL_BEGIN - -static NSString *const kRequestFieldArtifact = @"artifact"; -static NSString *const kRequestFieldAssertion = @"assertion"; -static NSString *const kRequestFieldAttestation = @"attestation_statement"; -static NSString *const kRequestFieldChallenge = @"challenge"; -static NSString *const kRequestFieldKeyID = @"key_id"; -static NSString *const kRequestFieldLimitedUse = @"limited_use"; - -static NSString *const kExchangeAppAttestAssertionEndpoint = @"exchangeAppAttestAssertion"; -static NSString *const kExchangeAppAttestAttestationEndpoint = @"exchangeAppAttestAttestation"; -static NSString *const kGenerateAppAttestChallengeEndpoint = @"generateAppAttestChallenge"; - -static NSString *const kContentTypeKey = @"Content-Type"; -static NSString *const kJSONContentType = @"application/json"; -static NSString *const kHTTPMethodPost = @"POST"; - -@interface GACAppAttestAPIService () - -@property(nonatomic, readonly) id APIService; - -@property(nonatomic, readonly) NSString *resourceName; - -// TODO(andrewheard): Remove or refactor property when short-lived token feature is implemented. -// When `YES`, forces a short-lived token with a 5 minute TTL. -@property(nonatomic, readonly) BOOL limitedUse; - -@end - -@implementation GACAppAttestAPIService - -- (instancetype)initWithAPIService:(id)APIService - resourceName:(NSString *)resourceName - limitedUse:(BOOL)limitedUse { - self = [super init]; - if (self) { - _APIService = APIService; - _resourceName = [resourceName copy]; - _limitedUse = limitedUse; - } - return self; -} - -#pragma mark - Assertion request - -- (FBLPromise *)getAppCheckTokenWithArtifact:(NSData *)artifact - challenge:(NSData *)challenge - assertion:(NSData *)assertion { - NSURL *URL = [self URLForEndpoint:kExchangeAppAttestAssertionEndpoint]; - - return [self HTTPBodyWithArtifact:artifact challenge:challenge assertion:assertion] - .then(^FBLPromise *(NSData *HTTPBody) { - return [self.APIService sendRequestWithURL:URL - HTTPMethod:kHTTPMethodPost - body:HTTPBody - additionalHeaders:@{kContentTypeKey : kJSONContentType}]; - }) - .then(^id _Nullable(GULURLSessionDataResponse *_Nullable response) { - return [self.APIService appCheckTokenWithAPIResponse:response]; - }); -} - -#pragma mark - Random Challenge - -- (nonnull FBLPromise *)getRandomChallenge { - NSURL *URL = [self URLForEndpoint:kGenerateAppAttestChallengeEndpoint]; - - return [FBLPromise onQueue:[self backgroundQueue] - do:^id _Nullable { - return [self.APIService sendRequestWithURL:URL - HTTPMethod:kHTTPMethodPost - body:nil - additionalHeaders:nil]; - }] - .then(^id _Nullable(GULURLSessionDataResponse *_Nullable response) { - return [self randomChallengeWithAPIResponse:response]; - }); -} - -#pragma mark - Challenge response parsing - -- (FBLPromise *)randomChallengeWithAPIResponse:(GULURLSessionDataResponse *)response { - return [FBLPromise onQueue:[self backgroundQueue] - do:^id _Nullable { - NSError *error; - - NSData *randomChallenge = - [self randomChallengeFromResponseBody:response.HTTPBody - error:&error]; - - return randomChallenge ?: error; - }]; -} - -- (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(NSError **)outError { - if (response.length <= 0) { - GACAppCheckSetErrorToPointer( - [GACAppCheckErrorUtil errorWithFailureReason:@"Empty server response body."], outError); - return nil; - } - - NSError *JSONError; - NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:response - options:0 - error:&JSONError]; - - if (![responseDict isKindOfClass:[NSDictionary class]]) { - GACAppCheckSetErrorToPointer([GACAppCheckErrorUtil JSONSerializationError:JSONError], outError); - return nil; - } - - NSString *challenge = responseDict[@"challenge"]; - if (![challenge isKindOfClass:[NSString class]]) { - GACAppCheckSetErrorToPointer( - [GACAppCheckErrorUtil appCheckTokenResponseErrorWithMissingField:@"challenge"], outError); - return nil; - } - - NSData *randomChallenge = [[NSData alloc] initWithBase64EncodedString:challenge options:0]; - return randomChallenge; -} - -#pragma mark - Attestation request - -- (FBLPromise *)attestKeyWithAttestation:(NSData *)attestation - keyID:(NSString *)keyID - challenge:(NSData *)challenge { - NSURL *URL = [self URLForEndpoint:kExchangeAppAttestAttestationEndpoint]; - - return [self HTTPBodyWithAttestation:attestation keyID:keyID challenge:challenge] - .then(^FBLPromise *(NSData *HTTPBody) { - return [self.APIService sendRequestWithURL:URL - HTTPMethod:kHTTPMethodPost - body:HTTPBody - additionalHeaders:@{kContentTypeKey : kJSONContentType}]; - }) - .thenOn( - [self backgroundQueue], ^id _Nullable(GULURLSessionDataResponse *_Nullable URLResponse) { - NSError *error; - - __auto_type response = - [[GACAppAttestAttestationResponse alloc] initWithResponseData:URLResponse.HTTPBody - requestDate:[NSDate date] - error:&error]; - - return response ?: error; - }); -} - -#pragma mark - Request HTTP Body - -- (FBLPromise *)HTTPBodyWithArtifact:(NSData *)artifact - challenge:(NSData *)challenge - assertion:(NSData *)assertion { - if (artifact.length <= 0 || challenge.length <= 0 || assertion.length <= 0) { - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise reject:[GACAppCheckErrorUtil - errorWithFailureReason:@"Missing or empty request parameter."]]; - return rejectedPromise; - } - - return [FBLPromise onQueue:[self backgroundQueue] - do:^id { - id JSONObject = @{ - kRequestFieldArtifact : [self base64StringWithData:artifact], - kRequestFieldChallenge : [self base64StringWithData:challenge], - kRequestFieldAssertion : [self base64StringWithData:assertion], - kRequestFieldLimitedUse : @(self.limitedUse) - }; - - return [self HTTPBodyWithJSONObject:JSONObject]; - }]; -} - -- (FBLPromise *)HTTPBodyWithAttestation:(NSData *)attestation - keyID:(NSString *)keyID - challenge:(NSData *)challenge { - if (attestation.length <= 0 || keyID.length <= 0 || challenge.length <= 0) { - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise reject:[GACAppCheckErrorUtil - errorWithFailureReason:@"Missing or empty request parameter."]]; - return rejectedPromise; - } - - return [FBLPromise onQueue:[self backgroundQueue] - do:^id { - id JSONObject = @{ - kRequestFieldKeyID : keyID, - kRequestFieldAttestation : [self base64StringWithData:attestation], - kRequestFieldChallenge : [self base64StringWithData:challenge], - kRequestFieldLimitedUse : @(self.limitedUse) - }; - - return [self HTTPBodyWithJSONObject:JSONObject]; - }]; -} - -- (FBLPromise *)HTTPBodyWithJSONObject:(nonnull id)JSONObject { - NSError *encodingError; - NSData *payloadJSON = [NSJSONSerialization dataWithJSONObject:JSONObject - options:0 - error:&encodingError]; - FBLPromise *HTTPBodyPromise = [FBLPromise pendingPromise]; - if (payloadJSON) { - [HTTPBodyPromise fulfill:payloadJSON]; - } else { - [HTTPBodyPromise reject:[GACAppCheckErrorUtil JSONSerializationError:encodingError]]; - } - return HTTPBodyPromise; -} - -#pragma mark - Helpers - -- (NSString *)base64StringWithData:(NSData *)data { - return [data base64EncodedStringWithOptions:0]; -} - -- (NSURL *)URLForEndpoint:(NSString *)endpoint { - NSString *URL = [[self class] URLWithBaseURL:self.APIService.baseURL - resourceName:self.resourceName]; - return [NSURL URLWithString:[NSString stringWithFormat:@"%@:%@", URL, endpoint]]; -} - -+ (NSString *)URLWithBaseURL:(NSString *)baseURL resourceName:(NSString *)resourceName { - return [NSString stringWithFormat:@"%@/%@", baseURL, resourceName]; -} - -- (dispatch_queue_t)backgroundQueue { - return dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h b/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h deleted file mode 100644 index 50bbf77df267..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h +++ /dev/null @@ -1,43 +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 - -@class GACAppCheckToken; - -NS_ASSUME_NONNULL_BEGIN - -@interface GACAppAttestAttestationResponse : NSObject - -/// App Attest attestation artifact required to refresh Firebase App Check token. -@property(nonatomic, readonly) NSData *artifact; - -/// Firebase App Check token. -@property(nonatomic, readonly) GACAppCheckToken *token; - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithArtifact:(NSData *)artifact - token:(GACAppCheckToken *)token NS_DESIGNATED_INITIALIZER; - -/// Init with the server response. -- (nullable instancetype)initWithResponseData:(NSData *)response - requestDate:(NSDate *)requestDate - error:(NSError **)outError; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.m b/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.m deleted file mode 100644 index 175e593974f3..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.m +++ /dev/null @@ -1,96 +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 "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h" - -#import "AppCheckCore/Sources/Core/APIService/GACAppCheckToken+APIResponse.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" - -static NSString *const kResponseFieldAppCheckTokenDict = @"appCheckToken"; -static NSString *const kResponseFieldArtifact = @"artifact"; - -@implementation GACAppAttestAttestationResponse - -- (instancetype)initWithArtifact:(NSData *)artifact token:(GACAppCheckToken *)token { - self = [super init]; - if (self) { - _artifact = artifact; - _token = token; - } - return self; -} - -- (nullable instancetype)initWithResponseData:(NSData *)response - requestDate:(NSDate *)requestDate - error:(NSError **)outError { - if (response.length <= 0) { - GACAppCheckSetErrorToPointer( - [GACAppCheckErrorUtil - errorWithFailureReason: - @"Failed to parse the initial handshake response. Empty server response body."], - outError); - return nil; - } - - NSError *JSONError; - NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:response - options:0 - error:&JSONError]; - - if (![responseDict isKindOfClass:[NSDictionary class]]) { - GACAppCheckSetErrorToPointer([GACAppCheckErrorUtil JSONSerializationError:JSONError], outError); - return nil; - } - - NSString *artifactBase64String = responseDict[kResponseFieldArtifact]; - if (![artifactBase64String isKindOfClass:[NSString class]]) { - GACAppCheckSetErrorToPointer( - [GACAppCheckErrorUtil - appAttestAttestationResponseErrorWithMissingField:kResponseFieldArtifact], - outError); - return nil; - } - NSData *artifactData = [[NSData alloc] initWithBase64EncodedString:artifactBase64String - options:0]; - if (artifactData == nil) { - GACAppCheckSetErrorToPointer( - [GACAppCheckErrorUtil - appAttestAttestationResponseErrorWithMissingField:kResponseFieldArtifact], - outError); - return nil; - } - - NSDictionary *appCheckTokenDict = responseDict[kResponseFieldAppCheckTokenDict]; - if (![appCheckTokenDict isKindOfClass:[NSDictionary class]]) { - GACAppCheckSetErrorToPointer( - [GACAppCheckErrorUtil - appAttestAttestationResponseErrorWithMissingField:kResponseFieldAppCheckTokenDict], - outError); - return nil; - } - - GACAppCheckToken *appCheckToken = [[GACAppCheckToken alloc] initWithResponseDict:appCheckTokenDict - requestDate:requestDate - error:outError]; - - if (appCheckToken == nil) { - return nil; - } - - return [self initWithArtifact:artifactData token:appCheckToken]; -} - -@end diff --git a/AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.h b/AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.h deleted file mode 100644 index b8f8510f3316..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.h +++ /dev/null @@ -1,37 +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 - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckAvailability.h" - -// Currently DCAppAttestService is available on iOS only. -#if GAC_APP_ATTEST_SUPPORTED_TARGETS - -#import - -#import "AppCheckCore/Sources/AppAttestProvider/GACAppAttestService.h" - -NS_ASSUME_NONNULL_BEGIN - -GAC_APP_ATTEST_PROVIDER_AVAILABILITY -@interface DCAppAttestService (GACAppAttestService) - -@end - -NS_ASSUME_NONNULL_END - -#endif // GAC_APP_ATTEST_SUPPORTED_TARGETS diff --git a/AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.m b/AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.m deleted file mode 100644 index 959ceab74085..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.m +++ /dev/null @@ -1,26 +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 "AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.h" - -// Currently DCAppAttestService is available on iOS only. -#if GAC_APP_ATTEST_SUPPORTED_TARGETS - -@implementation DCAppAttestService (GACAppAttestService) - -@end - -#endif // GAC_APP_ATTEST_SUPPORTED_TARGETS diff --git a/AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.h b/AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.h deleted file mode 100644 index c4213df898d4..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.h +++ /dev/null @@ -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 - -NS_ASSUME_NONNULL_BEGIN - -@interface GACAppAttestRejectionError : NSError - -- (instancetype)init; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.m b/AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.m deleted file mode 100644 index 93f5f95a9f9e..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.m +++ /dev/null @@ -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 "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h" - -#import "AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.h" - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" - -@implementation GACAppAttestRejectionError - -- (instancetype)init { - return [self initWithDomain:GACAppCheckErrorDomain code:GACAppCheckErrorCodeUnknown userInfo:nil]; -} - -@end diff --git a/AppCheckCore/Sources/AppAttestProvider/GACAppAttestProvider.m b/AppCheckCore/Sources/AppAttestProvider/GACAppAttestProvider.m deleted file mode 100644 index 336891780e27..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/GACAppAttestProvider.m +++ /dev/null @@ -1,522 +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 "AppCheckCore/Sources/Public/AppCheckCore/GACAppAttestProvider.h" - -#import "AppCheckCore/Sources/AppAttestProvider/DCAppAttestService+GACAppAttestService.h" - -#if __has_include() -#import -#else -#import "FBLPromises.h" -#endif - -#import "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.h" -#import "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h" -#import "AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.h" -#import "AppCheckCore/Sources/AppAttestProvider/GACAppAttestService.h" -#import "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.h" -#import "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.h" -#import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h" -#import "AppCheckCore/Sources/Core/Backoff/GACAppCheckBackoffWrapper.h" -#import "AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h" - -#import "AppCheckCore/Sources/Core/Utils/GACAppCheckCryptoUtils.h" - -#import "AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h" - -NS_ASSUME_NONNULL_BEGIN - -/// A data object that contains all key attest data required for FAC token exchange. -@interface GACAppAttestKeyAttestationResult : NSObject - -@property(nonatomic, readonly) NSString *keyID; -@property(nonatomic, readonly) NSData *challenge; -@property(nonatomic, readonly) NSData *attestation; - -- (instancetype)initWithKeyID:(NSString *)keyID - challenge:(NSData *)challenge - attestation:(NSData *)attestation; - -@end - -@implementation GACAppAttestKeyAttestationResult - -- (instancetype)initWithKeyID:(NSString *)keyID - challenge:(NSData *)challenge - attestation:(NSData *)attestation { - self = [super init]; - if (self) { - _keyID = keyID; - _challenge = challenge; - _attestation = attestation; - } - return self; -} - -@end - -/// A data object that contains information required for assertion request. -@interface GACAppAttestAssertionData : NSObject - -@property(nonatomic, readonly) NSData *challenge; -@property(nonatomic, readonly) NSData *artifact; -@property(nonatomic, readonly) NSData *assertion; - -- (instancetype)initWithChallenge:(NSData *)challenge - artifact:(NSData *)artifact - assertion:(NSData *)assertion; - -@end - -@implementation GACAppAttestAssertionData - -- (instancetype)initWithChallenge:(NSData *)challenge - artifact:(NSData *)artifact - assertion:(NSData *)assertion { - self = [super init]; - if (self) { - _challenge = challenge; - _artifact = artifact; - _assertion = assertion; - } - return self; -} - -@end - -@interface GACAppAttestProvider () - -@property(nonatomic, readonly) id APIService; -@property(nonatomic, readonly) id appAttestService; -@property(nonatomic, readonly) id keyIDStorage; -@property(nonatomic, readonly) id artifactStorage; -@property(nonatomic, readonly) id backoffWrapper; - -@property(nonatomic, nullable) FBLPromise *ongoingGetTokenOperation; - -@property(nonatomic, readonly) dispatch_queue_t queue; - -@end - -@implementation GACAppAttestProvider - -- (instancetype)initWithAppAttestService:(id)appAttestService - APIService:(id)APIService - keyIDStorage:(id)keyIDStorage - artifactStorage:(id)artifactStorage - backoffWrapper:(id)backoffWrapper { - self = [super init]; - if (self) { - _appAttestService = appAttestService; - _APIService = APIService; - _keyIDStorage = keyIDStorage; - _artifactStorage = artifactStorage; - _backoffWrapper = backoffWrapper; - _queue = dispatch_queue_create("com.google.GACAppAttestProvider", DISPATCH_QUEUE_SERIAL); - } - return self; -} - -- (nullable instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - baseURL:(nullable NSString *)baseURL - APIKey:(nullable NSString *)APIKey - keychainAccessGroup:(nullable NSString *)accessGroup - requestHooks: - (nullable NSArray *)requestHooks { - return [self initWithServiceName:serviceName - resourceName:resourceName - baseURL:baseURL - APIKey:APIKey - keychainAccessGroup:accessGroup - limitedUse:NO - requestHooks:requestHooks]; -} - -- (nullable instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - baseURL:(nullable NSString *)baseURL - APIKey:(nullable NSString *)APIKey - keychainAccessGroup:(nullable NSString *)accessGroup - limitedUse:(BOOL)limitedUse - requestHooks: - (nullable NSArray *)requestHooks { -#if GAC_APP_ATTEST_SUPPORTED_TARGETS - NSURLSession *URLSession = [NSURLSession - sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; - - NSString *storageKeySuffix = [GACAppAttestProvider storageKeySuffixWithServiceName:serviceName - resourceName:resourceName]; - - GACAppAttestKeyIDStorage *keyIDStorage = - [[GACAppAttestKeyIDStorage alloc] initWithKeySuffix:storageKeySuffix]; - - GACAppCheckAPIService *APIService = - [[GACAppCheckAPIService alloc] initWithURLSession:URLSession - baseURL:baseURL - APIKey:APIKey - requestHooks:requestHooks]; - - GACAppAttestAPIService *appAttestAPIService = - [[GACAppAttestAPIService alloc] initWithAPIService:APIService - resourceName:resourceName - limitedUse:limitedUse]; - - GACAppAttestArtifactStorage *artifactStorage = - [[GACAppAttestArtifactStorage alloc] initWithKeySuffix:storageKeySuffix - accessGroup:accessGroup]; - - GACAppCheckBackoffWrapper *backoffWrapper = [[GACAppCheckBackoffWrapper alloc] init]; - - return [self initWithAppAttestService:DCAppAttestService.sharedService - APIService:appAttestAPIService - keyIDStorage:keyIDStorage - artifactStorage:artifactStorage - backoffWrapper:backoffWrapper]; -#else // GAC_APP_ATTEST_SUPPORTED_TARGETS - return nil; -#endif // GAC_APP_ATTEST_SUPPORTED_TARGETS -} - -#pragma mark - GACAppCheckProvider - -- (void)getTokenWithCompletion:(void (^)(GACAppCheckToken *_Nullable, NSError *_Nullable))handler { - [self getToken] - // Call the handler with the result. - .then(^FBLPromise *(GACAppCheckToken *token) { - handler(token, nil); - return nil; - }) - .catch(^(NSError *error) { - handler(nil, error); - }); -} - -- (FBLPromise *)getToken { - return [FBLPromise onQueue:self.queue - do:^id _Nullable { - if (self.ongoingGetTokenOperation == nil) { - // Kick off a new handshake sequence only when there is not an ongoing - // handshake to avoid race conditions. - self.ongoingGetTokenOperation = - [self createGetTokenSequenceWithBackoffPromise] - - // Release the ongoing operation promise on completion. - .then(^GACAppCheckToken *(GACAppCheckToken *token) { - self.ongoingGetTokenOperation = nil; - return token; - }) - .recover(^NSError *(NSError *error) { - self.ongoingGetTokenOperation = nil; - return error; - }); - } - return self.ongoingGetTokenOperation; - }]; -} - -- (FBLPromise *)createGetTokenSequenceWithBackoffPromise { - return [self.backoffWrapper - applyBackoffToOperation:^FBLPromise *_Nonnull { - return [self createGetTokenSequencePromise]; - } - errorHandler:[self.backoffWrapper defaultAppCheckProviderErrorHandler]]; -} - -- (FBLPromise *)createGetTokenSequencePromise { - // Check attestation state to decide on the next steps. - return [self attestationState].thenOn(self.queue, ^id(GACAppAttestProviderState *attestState) { - switch (attestState.state) { - case GACAppAttestAttestationStateUnsupported: - GACAppCheckLogDebug(GACLoggerAppCheckMessageCodeAppAttestNotSupported, - @"App Attest is not supported."); - return attestState.appAttestUnsupportedError; - break; - - case GACAppAttestAttestationStateSupportedInitial: - case GACAppAttestAttestationStateKeyGenerated: - // Initial handshake is required for both the "initial" and the "key generated" states. - return [self initialHandshakeWithKeyID:attestState.appAttestKeyID]; - break; - - case GACAppAttestAttestationStateKeyRegistered: - // Refresh FAC token using the existing registered App Attest key pair. - return [self refreshTokenWithKeyID:attestState.appAttestKeyID - artifact:attestState.attestationArtifact]; - break; - } - }); -} - -#pragma mark - Initial handshake sequence (attestation) - -- (FBLPromise *)initialHandshakeWithKeyID:(nullable NSString *)keyID { - // 1. Attest the device. Retry once on 403 from Firebase backend (attestation rejection error). - __block NSString *keyIDForAttempt = keyID; - return [FBLPromise onQueue:self.queue - attempts:1 - delay:0 - condition:^BOOL(NSInteger attemptCount, NSError *_Nonnull error) { - // Reset keyID before retrying. - keyIDForAttempt = nil; - return [error isKindOfClass:[GACAppAttestRejectionError class]]; - } - retry:^FBLPromise *_Nullable { - return [self attestKeyGenerateIfNeededWithID:keyIDForAttempt]; - }] - .thenOn(self.queue, ^FBLPromise *(NSArray *attestationResults) { - // 4. Save the artifact and return the received FAC token. - - GACAppAttestKeyAttestationResult *attestation = attestationResults.firstObject; - GACAppAttestAttestationResponse *firebaseAttestationResponse = - attestationResults.lastObject; - - return [self saveArtifactAndGetAppCheckTokenFromResponse:firebaseAttestationResponse - keyID:attestation.keyID]; - }); -} - -- (FBLPromise *)saveArtifactAndGetAppCheckTokenFromResponse: - (GACAppAttestAttestationResponse *)response - keyID:(NSString *)keyID { - return [self.artifactStorage setArtifact:response.artifact forKey:keyID].thenOn( - self.queue, ^GACAppCheckToken *(id result) { - return response.token; - }); -} - -- (FBLPromise *)attestKey:(NSString *)keyID - challenge:(NSData *)challenge { - return [FBLPromise onQueue:self.queue - do:^NSData *_Nullable { - return [GACAppCheckCryptoUtils sha256HashFromData:challenge]; - }] - .thenOn( - self.queue, - ^FBLPromise *(NSData *challengeHash) { - return [FBLPromise onQueue:self.queue - wrapObjectOrErrorCompletion:^(FBLPromiseObjectOrErrorCompletion _Nonnull handler) { - [self.appAttestService attestKey:keyID - clientDataHash:challengeHash - completionHandler:handler]; - }]; - }) - .thenOn(self.queue, ^FBLPromise *(NSData *attestation) { - GACAppAttestKeyAttestationResult *result = - [[GACAppAttestKeyAttestationResult alloc] initWithKeyID:keyID - challenge:challenge - attestation:attestation]; - return [FBLPromise resolvedWith:result]; - }); -} - -- (FBLPromise *)attestKeyGenerateIfNeededWithID: - (nullable NSString *)keyID { - // 1. Request a random challenge and get App Attest key ID concurrently. - return [FBLPromise onQueue:self.queue - all:@[ - // 1.1. Request random challenge. - [self.APIService getRandomChallenge], - // 1.2. Get App Attest key ID. - [self generateAppAttestKeyIDIfNeeded:keyID] - ]] - .thenOn(self.queue, - ^FBLPromise *(NSArray *challengeAndKeyID) { - // 2. Attest the key. - NSData *challenge = challengeAndKeyID.firstObject; - NSString *keyID = challengeAndKeyID.lastObject; - - return [self attestKey:keyID challenge:challenge]; - }) - .thenOn(self.queue, - ^FBLPromise *(GACAppAttestKeyAttestationResult *result) { - // 3. Exchange the attestation to FAC token and pass the results to the next step. - NSArray *attestationResults = @[ - // 3.1. Just pass the attestation result to the next step. - [FBLPromise resolvedWith:result], - // 3.2. Exchange the attestation to FAC token. - [self.APIService attestKeyWithAttestation:result.attestation - keyID:result.keyID - challenge:result.challenge] - ]; - - return [FBLPromise onQueue:self.queue all:attestationResults]; - }) - .recoverOn(self.queue, ^id(NSError *error) { - // If App Attest attestation was rejected then reset the attestation and throw a specific - // error. - GACAppCheckHTTPError *HTTPError = (GACAppCheckHTTPError *)error; - if ([HTTPError isKindOfClass:[GACAppCheckHTTPError class]] && - HTTPError.HTTPResponse.statusCode == 403) { - GACAppCheckLogDebug(GACLoggerAppCheckMessageCodeAttestationRejected, - @"App Attest attestation was rejected by backend. The existing " - @"attestation will be reset."); - // Reset the attestation. - return [self resetAttestation].thenOn(self.queue, ^NSError *(id result) { - // Throw the rejection error. - return [[GACAppAttestRejectionError alloc] init]; - }); - } - - // Otherwise just re-throw the error. - return error; - }); -} - -/// Resets stored key ID and attestation artifact. -- (FBLPromise *)resetAttestation { - return [self.keyIDStorage setAppAttestKeyID:nil].thenOn(self.queue, ^id(id result) { - return [self.artifactStorage setArtifact:nil forKey:@""]; - }); -} - -#pragma mark - Token refresh sequence (assertion) - -- (FBLPromise *)refreshTokenWithKeyID:(NSString *)keyID - artifact:(NSData *)artifact { - return [self.APIService getRandomChallenge] - .thenOn(self.queue, - ^FBLPromise *(NSData *challenge) { - return [self generateAssertionWithKeyID:keyID - artifact:artifact - challenge:challenge]; - }) - .thenOn(self.queue, ^id(GACAppAttestAssertionData *assertion) { - return [self.APIService getAppCheckTokenWithArtifact:assertion.artifact - challenge:assertion.challenge - assertion:assertion.assertion]; - }); -} - -- (FBLPromise *)generateAssertionWithKeyID:(NSString *)keyID - artifact:(NSData *)artifact - challenge:(NSData *)challenge { - // 1. Calculate the statement and its hash for assertion. - return [FBLPromise - onQueue:self.queue - do:^NSData *_Nullable { - // 1.1. Compose statement to generate assertion for. - NSMutableData *statementForAssertion = [artifact mutableCopy]; - [statementForAssertion appendData:challenge]; - - // 1.2. Get the statement SHA256 hash. - return [GACAppCheckCryptoUtils sha256HashFromData:[statementForAssertion copy]]; - }] - .thenOn( - self.queue, - ^FBLPromise *(NSData *statementHash) { - // 2. Generate App Attest assertion. - return [FBLPromise onQueue:self.queue - wrapObjectOrErrorCompletion:^(FBLPromiseObjectOrErrorCompletion _Nonnull handler) { - [self.appAttestService generateAssertion:keyID - clientDataHash:statementHash - completionHandler:handler]; - }]; - }) - // 3. Compose the result object. - .thenOn(self.queue, ^GACAppAttestAssertionData *(NSData *assertion) { - return [[GACAppAttestAssertionData alloc] initWithChallenge:challenge - artifact:artifact - assertion:assertion]; - }); -} - -#pragma mark - State handling - -- (FBLPromise *)attestationState { - dispatch_queue_t stateQueue = - dispatch_queue_create("GACAppAttestProvider.state", DISPATCH_QUEUE_SERIAL); - - return [FBLPromise - onQueue:stateQueue - do:^id _Nullable { - NSError *error; - - // 1. Check if App Attest is supported. - id isSupportedResult = FBLPromiseAwait([self isAppAttestSupported], &error); - if (isSupportedResult == nil) { - return [[GACAppAttestProviderState alloc] initUnsupportedWithError:error]; - } - - // 2. Check for stored key ID of the generated App Attest key pair. - NSString *appAttestKeyID = - FBLPromiseAwait([self.keyIDStorage getAppAttestKeyID], &error); - if (appAttestKeyID == nil) { - return [[GACAppAttestProviderState alloc] initWithSupportedInitialState]; - } - - // 3. Check for stored attestation artifact received from Firebase backend. - NSData *attestationArtifact = - FBLPromiseAwait([self.artifactStorage getArtifactForKey:appAttestKeyID], &error); - if (attestationArtifact == nil) { - return [[GACAppAttestProviderState alloc] initWithGeneratedKeyID:appAttestKeyID]; - } - - // 4. A valid App Attest key pair was generated and registered with Firebase - // backend. Return the corresponding state. - return [[GACAppAttestProviderState alloc] initWithRegisteredKeyID:appAttestKeyID - artifact:attestationArtifact]; - }]; -} - -#pragma mark - Helpers - -/// Returns a resolved promise if App Attest is supported and a rejected promise if it is not. -- (FBLPromise *)isAppAttestSupported { - if (self.appAttestService.isSupported) { - return [FBLPromise resolvedWith:[NSNull null]]; - } else { - NSError *error = [GACAppCheckErrorUtil unsupportedAttestationProvider:@"AppAttestProvider"]; - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise reject:error]; - return rejectedPromise; - } -} - -/// Generates a new App Attest key associated with the Firebase app if `storedKeyID == nil`. -- (FBLPromise *)generateAppAttestKeyIDIfNeeded:(nullable NSString *)storedKeyID { - if (storedKeyID) { - // The key ID has been fetched already, just return it. - return [FBLPromise resolvedWith:storedKeyID]; - } else { - // Generate and save a new key otherwise. - return [self generateAppAttestKey]; - } -} - -/// Generates and stores App Attest key associated with the Firebase app. -- (FBLPromise *)generateAppAttestKey { - return [FBLPromise onQueue:self.queue - wrapObjectOrErrorCompletion:^(FBLPromiseObjectOrErrorCompletion _Nonnull handler) { - [self.appAttestService generateKeyWithCompletionHandler:handler]; - }] - .thenOn(self.queue, ^FBLPromise *(NSString *keyID) { - return [self.keyIDStorage setAppAttestKeyID:keyID]; - }); -} - -+ (NSString *)storageKeySuffixWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName { - return [NSString stringWithFormat:@"%@.%@", serviceName, resourceName]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.h b/AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.h deleted file mode 100644 index 4d255a348db9..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.h +++ /dev/null @@ -1,73 +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 - -NS_ASSUME_NONNULL_BEGIN - -/// Represents different stages of App Attest attestation. -typedef NS_ENUM(NSInteger, GACAppAttestAttestationState) { - /// App Attest is not supported on the current device. - GACAppAttestAttestationStateUnsupported, - - /// App Attest is supported, the App Attest key pair has been generated. - GACAppAttestAttestationStateSupportedInitial, - - /// App Attest key pair has been generated but has not been attested and registered with Firebase - /// backend. - GACAppAttestAttestationStateKeyGenerated, - - /// App Attest key has been generated, attested with Apple backend and registered with Firebase - /// backend. An encrypted artifact required to refresh FAC token is stored on the device. - GACAppAttestAttestationStateKeyRegistered, -}; - -/// Represents attestation stages of App Attest. The class is designed to be used exclusively by -/// `GACAppAttestProvider`. -@interface GACAppAttestProviderState : NSObject - -/// App Attest attestation state. -@property(nonatomic, readonly) GACAppAttestAttestationState state; - -/// An error object when state is GACAppAttestAttestationStateUnsupported. -@property(nonatomic, nullable, readonly) NSError *appAttestUnsupportedError; - -/// An App Attest key ID when state is GACAppAttestAttestationStateKeyGenerated or -/// GACAppAttestAttestationStateKeyRegistered. -@property(nonatomic, nullable, readonly) NSString *appAttestKeyID; - -/// An attestation artifact received from Firebase backend when state is -/// GACAppAttestAttestationStateKeyRegistered. -@property(nonatomic, nullable, readonly) NSData *attestationArtifact; - -- (instancetype)init NS_UNAVAILABLE; - -/// Init with GACAppAttestAttestationStateUnsupported and an error describing issue. -- (instancetype)initUnsupportedWithError:(NSError *)error; - -/// Init with GACAppAttestAttestationStateSupportedInitial. -- (instancetype)initWithSupportedInitialState; - -/// Init with GACAppAttestAttestationStateKeyGenerated and the key ID. -- (instancetype)initWithGeneratedKeyID:(NSString *)keyID; - -/// Init with GACAppAttestAttestationStateKeyRegistered, the key ID and the attestation artifact -/// received from Firebase backend. -- (instancetype)initWithRegisteredKeyID:(NSString *)keyID artifact:(NSData *)artifact; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.m b/AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.m deleted file mode 100644 index 0681ba57fad3..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.m +++ /dev/null @@ -1,57 +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 "AppCheckCore/Sources/AppAttestProvider/GACAppAttestProviderState.h" - -@implementation GACAppAttestProviderState - -- (instancetype)initUnsupportedWithError:(NSError *)error { - self = [super init]; - if (self) { - _state = GACAppAttestAttestationStateUnsupported; - _appAttestUnsupportedError = error; - } - return self; -} - -- (instancetype)initWithSupportedInitialState { - self = [super init]; - if (self) { - _state = GACAppAttestAttestationStateSupportedInitial; - } - return self; -} - -- (instancetype)initWithGeneratedKeyID:(NSString *)keyID { - self = [super init]; - if (self) { - _state = GACAppAttestAttestationStateKeyGenerated; - _appAttestKeyID = keyID; - } - return self; -} - -- (instancetype)initWithRegisteredKeyID:(NSString *)keyID artifact:(NSData *)artifact { - self = [super init]; - if (self) { - _state = GACAppAttestAttestationStateKeyRegistered; - _appAttestKeyID = keyID; - _attestationArtifact = artifact; - } - return self; -} - -@end diff --git a/AppCheckCore/Sources/AppAttestProvider/GACAppAttestService.h b/AppCheckCore/Sources/AppAttestProvider/GACAppAttestService.h deleted file mode 100644 index a2d3d2c01813..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/GACAppAttestService.h +++ /dev/null @@ -1,42 +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 - -@class FBLPromise; - -NS_ASSUME_NONNULL_BEGIN - -/// See `DCAppAttestService` -/// https://developer.apple.com/documentation/devicecheck/dcappattestservice?language=objc -@protocol GACAppAttestService - -@property(getter=isSupported, readonly) BOOL supported; - -- (void)generateKeyWithCompletionHandler:(void (^)(NSString *keyId, - NSError *error))completionHandler; - -- (void)attestKey:(NSString *)keyId - clientDataHash:(NSData *)clientDataHash - completionHandler:(void (^)(NSData *attestationObject, NSError *error))completionHandler; - -- (void)generateAssertion:(NSString *)keyId - clientDataHash:(NSData *)clientDataHash - completionHandler:(void (^)(NSData *assertionObject, NSError *error))completionHandler; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.h b/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.h deleted file mode 100644 index c9a312bdbb4c..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.h +++ /dev/null @@ -1,68 +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 - -@class FBLPromise; -@class GULKeychainStorage; - -NS_ASSUME_NONNULL_BEGIN - -/// Defines API of a storage capable to store an encrypted artifact required to refresh Firebase App -/// Check token obtained with App Attest provider. -@protocol GACAppAttestArtifactStorageProtocol - -/// Set the artifact. An artifact previously set for *any* key ID will be replaced by the new one -/// with the new key ID. The storage always stores a single artifact. -/// @param artifact The artifact data to store. Pass `nil` to remove the stored artifact. -/// @param keyID The App Attest key ID used to generate the artifact. -/// @return An artifact that is resolved with the artifact data passed into the method in case of -/// success or is rejected with an error. -- (FBLPromise *)setArtifact:(nullable NSData *)artifact forKey:(NSString *)keyID; - -/// Get the artifact. -/// @param keyID The App Attest key ID used to generate the artifact. -/// @return A promise that is resolved with the artifact data if artifact exists, is resolved with -/// `nil` if no artifact found (or the existing artifact was set for a different key ID) or is -/// rejected with an error. -- (FBLPromise *)getArtifactForKey:(NSString *)keyID; - -@end - -/// An implementation of GACAppAttestArtifactStorageProtocol. -@interface GACAppAttestArtifactStorage : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -/// Default convenience initializer. -/// @param keySuffix A unique suffix that will be used as a part of the key to store the token for -/// the storage instance. -/// @param accessGroup The Keychain Access Group. -- (instancetype)initWithKeySuffix:(NSString *)keySuffix - accessGroup:(nullable NSString *)accessGroup; - -/// Designated initializer. -/// @param keySuffix A unique suffix that will be used as a part of the key to store the token for -/// the storage instance. -/// @param keychainStorage An instance of `GULKeychainStorage` used as an underlying secure storage. -/// @param accessGroup The Keychain Access Group. -- (instancetype)initWithKeySuffix:(NSString *)keySuffix - keychainStorage:(GULKeychainStorage *)keychainStorage - accessGroup:(nullable NSString *)accessGroup NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.m b/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.m deleted file mode 100644 index 6d25b7b64da7..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.m +++ /dev/null @@ -1,117 +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 "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.h" - -#if __has_include() -#import -#else -#import "FBLPromises.h" -#endif - -#import - -#import "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" - -NS_ASSUME_NONNULL_BEGIN - -static NSString *const kKeychainService = @"com.firebase.app_check.app_attest_artifact_storage"; - -@interface GACAppAttestArtifactStorage () - -@property(nonatomic, readonly) NSString *keySuffix; -@property(nonatomic, readonly) GULKeychainStorage *keychainStorage; -@property(nonatomic, readonly, nullable) NSString *accessGroup; - -@end - -@implementation GACAppAttestArtifactStorage - -- (instancetype)initWithKeySuffix:(NSString *)keySuffix - keychainStorage:(GULKeychainStorage *)keychainStorage - accessGroup:(nullable NSString *)accessGroup { - self = [super init]; - if (self) { - _keySuffix = [keySuffix copy]; - _keychainStorage = keychainStorage; - _accessGroup = [accessGroup copy]; - } - return self; -} - -- (instancetype)initWithKeySuffix:(NSString *)keySuffix - accessGroup:(nullable NSString *)accessGroup { - GULKeychainStorage *keychainStorage = - [[GULKeychainStorage alloc] initWithService:kKeychainService]; - return [self initWithKeySuffix:keySuffix keychainStorage:keychainStorage accessGroup:accessGroup]; -} - -- (FBLPromise *)getArtifactForKey:(NSString *)keyID { - return [self.keychainStorage getObjectForKey:[self artifactKey] - objectClass:[GACAppAttestStoredArtifact class] - accessGroup:self.accessGroup] - .then(^NSData *(id storedArtifact) { - GACAppAttestStoredArtifact *artifact = (GACAppAttestStoredArtifact *)storedArtifact; - if ([artifact isKindOfClass:[GACAppAttestStoredArtifact class]] && - [artifact.keyID isEqualToString:keyID]) { - return artifact.artifact; - } else { - return nil; - } - }) - .recover(^NSError *(NSError *error) { - return [GACAppCheckErrorUtil keychainErrorWithError:error]; - }); -} - -- (FBLPromise *)setArtifact:(nullable NSData *)artifact forKey:(nonnull NSString *)keyID { - if (artifact) { - return [self storeArtifact:artifact forKey:keyID].recover(^NSError *(NSError *error) { - return [GACAppCheckErrorUtil keychainErrorWithError:error]; - }); - } else { - return [self.keychainStorage removeObjectForKey:[self artifactKey] accessGroup:self.accessGroup] - .then(^id _Nullable(NSNull *_Nullable value) { - return nil; - }) - .recover(^NSError *(NSError *error) { - return [GACAppCheckErrorUtil keychainErrorWithError:error]; - }); - } -} - -#pragma mark - Helpers - -- (FBLPromise *)storeArtifact:(nullable NSData *)artifact - forKey:(nonnull NSString *)keyID { - GACAppAttestStoredArtifact *storedArtifact = - [[GACAppAttestStoredArtifact alloc] initWithKeyID:keyID artifact:artifact]; - return [self.keychainStorage setObject:storedArtifact - forKey:[self artifactKey] - accessGroup:self.accessGroup] - .then(^id _Nullable(NSNull *_Nullable value) { - return artifact; - }); -} - -- (NSString *)artifactKey { - return [NSString stringWithFormat:@"app_check_app_attest_artifact.%@", self.keySuffix]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.h b/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.h deleted file mode 100644 index dcbc7a3b26a7..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.h +++ /dev/null @@ -1,58 +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 - -@class FBLPromise; - -NS_ASSUME_NONNULL_BEGIN - -/// The protocol defines methods to store App Attest key IDs per Firebase app. -@protocol GACAppAttestKeyIDStorageProtocol - -/** Manages storage of an app attest key ID. - * @param keyID The app attest key ID to store or `nil` to remove the existing app attest key ID. - * @return A promise that is resolved with a stored app attest key ID or `nil` if the existing app - * attest key ID has been removed. - */ -- (FBLPromise *)setAppAttestKeyID:(nullable NSString *)keyID; - -/** Reads a stored app attest key ID. - * @return A promise that is resolved with a stored app attest key ID or `nil` if there is not a - * stored app attest key ID. The promise is rejected with an error in the case of a missing app - * attest key ID . - */ -- (FBLPromise *)getAppAttestKeyID; - -@end - -/// The App Attest key ID storage implementation. -/// This class is designed for use by `GACAppAttestProvider`. It's operations are managed by -/// `GACAppAttestProvider`'s internal serial queue. It is not considered thread safe and should not -/// be used by other classes at this time. -@interface GACAppAttestKeyIDStorage : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -/** Default convenience initializer. - * @param keySuffix A unique suffix that will be used as a part of the key to store the token for - * the storage instance. - */ -- (instancetype)initWithKeySuffix:(NSString *)keySuffix; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.m b/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.m deleted file mode 100644 index 76723f10a142..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.m +++ /dev/null @@ -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 "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.h" - -#if __has_include() -#import -#else -#import "FBLPromises.h" -#endif - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" - -/// The `NSUserDefaults` suite name for the storage location of the app attest key ID. -static NSString *const kKeyIDStorageDefaultsSuiteName = @"com.firebase.GACAppAttestKeyIDStorage"; - -@interface GACAppAttestKeyIDStorage () - -@property(nonatomic, readonly) NSString *keySuffix; - -/// The app attest key ID is stored using `NSUserDefaults` . -@property(nonatomic, readonly) NSUserDefaults *userDefaults; - -@end - -@implementation GACAppAttestKeyIDStorage - -- (instancetype)initWithKeySuffix:(NSString *)keySuffix { - self = [super init]; - if (self) { - _keySuffix = [keySuffix copy]; - _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:kKeyIDStorageDefaultsSuiteName]; - } - return self; -} - -- (nonnull FBLPromise *)setAppAttestKeyID:(nullable NSString *)keyID { - [self storeAppAttestKeyID:keyID]; - return [FBLPromise resolvedWith:keyID]; -} - -- (nonnull FBLPromise *)getAppAttestKeyID { - NSString *appAttestKeyID = [self appAttestKeyIDFromStorage]; - if (appAttestKeyID) { - return [FBLPromise resolvedWith:appAttestKeyID]; - } else { - NSError *error = [GACAppCheckErrorUtil appAttestKeyIDNotFound]; - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise reject:error]; - return rejectedPromise; - } -} - -#pragma mark - Helpers - -- (void)storeAppAttestKeyID:(nullable NSString *)keyID { - if (keyID) { - [self.userDefaults setObject:keyID forKey:[self keyIDStorageKey]]; - } else { - [self.userDefaults removeObjectForKey:[self keyIDStorageKey]]; - } -} - -- (nullable NSString *)appAttestKeyIDFromStorage { - NSString *appAttestKeyID = nil; - appAttestKeyID = [self.userDefaults objectForKey:[self keyIDStorageKey]]; - return appAttestKeyID; -} - -- (NSString *)keyIDStorageKey { - return [NSString stringWithFormat:@"app_attest_keyID.%@", self.keySuffix]; -} - -@end diff --git a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.h b/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.h deleted file mode 100644 index d7895b5e0387..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.h +++ /dev/null @@ -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 - -NS_ASSUME_NONNULL_BEGIN - -@interface GACAppAttestStoredArtifact : NSObject - -/// The App Attest key ID used to generate the artifact. -@property(nonatomic, readonly) NSString *keyID; - -/// The Firebase App Attest artifact generated by the backend. -@property(nonatomic, readonly) NSData *artifact; - -/// The object version. -/// WARNING: The version must be incremented if properties are added, removed or modified. Migration -/// must be handled accordingly in `initWithCoder:` method. -@property(nonatomic, readonly) NSInteger storageVersion; - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithKeyID:(NSString *)keyID - artifact:(NSData *)artifact NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.m b/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.m deleted file mode 100644 index 8864601622d4..000000000000 --- a/AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.m +++ /dev/null @@ -1,74 +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 "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestStoredArtifact.h" - -static NSString *const kKeyIDKey = @"keyID"; -static NSString *const kArtifactKey = @"artifact"; -static NSString *const kStorageVersionKey = @"storageVersion"; - -static NSInteger const kStorageVersion = 1; - -@implementation GACAppAttestStoredArtifact - -- (instancetype)initWithKeyID:(NSString *)keyID artifact:(NSData *)artifact { - self = [super init]; - if (self) { - _keyID = keyID; - _artifact = artifact; - } - return self; -} - -- (NSInteger)storageVersion { - return kStorageVersion; -} - -#pragma mark - NSSecureCoding - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (void)encodeWithCoder:(nonnull NSCoder *)coder { - [coder encodeObject:self.keyID forKey:kKeyIDKey]; - [coder encodeObject:self.artifact forKey:kArtifactKey]; - [coder encodeInteger:self.storageVersion forKey:kStorageVersionKey]; -} - -- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder { - NSInteger storageVersion = [coder decodeIntegerForKey:kStorageVersionKey]; - - if (storageVersion < kStorageVersion) { - // Handle migration here when new versions are added - } - - // If the version of the stored object is equal or higher than the current version then try the - // best to get enough data to initialize the object. - NSString *keyID = [coder decodeObjectOfClass:[NSString class] forKey:kKeyIDKey]; - if (keyID.length < 1) { - return nil; - } - - NSData *artifact = [coder decodeObjectOfClass:[NSData class] forKey:kArtifactKey]; - if (artifact.length < 1) { - return nil; - } - - return [self initWithKeyID:keyID artifact:artifact]; -} - -@end diff --git a/AppCheckCore/Sources/Core/Backoff/GACAppCheckBackoffWrapper.h b/AppCheckCore/Sources/Core/Backoff/GACAppCheckBackoffWrapper.h deleted file mode 100644 index 8b34e2983a58..000000000000 --- a/AppCheckCore/Sources/Core/Backoff/GACAppCheckBackoffWrapper.h +++ /dev/null @@ -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 - -@class FBLPromise; - -NS_ASSUME_NONNULL_BEGIN - -/// Backoff type. Backoff interval calculation depends on the type. -typedef NS_ENUM(NSUInteger, GACAppCheckBackoffType) { - /// No backoff. Another retry is allowed straight away. - GACAppCheckBackoffTypeNone, - - /// Next retry will be allowed in 1 day (24 hours) after the failure. - GACAppCheckBackoffType1Day, - - /// A small backoff interval that exponentially increases after each consequent failure. - GACAppCheckBackoffTypeExponential -}; - -/// Creates a promise for an operation to apply the backoff to. -typedef FBLPromise *_Nonnull (^GACAppCheckBackoffOperationProvider)(void); - -/// Converts an error to a backoff type. -typedef GACAppCheckBackoffType (^GACAppCheckBackoffErrorHandler)(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 (^GACAppCheckDateProvider)(void); - -/// Defines API for an object that conditionally applies backoff to a given operation based on the -/// history of previous operation failures. -@protocol GACAppCheckBackoffWrapperProtocol - -/// 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:(GACAppCheckBackoffOperationProvider)operationProvider - errorHandler:(GACAppCheckBackoffErrorHandler)errorHandler; - -/// The default Firebase services error handler. It keeps track of network errors and -/// `GACAppCheckHTTPError.HTTPResponse.statusCode.statusCode` value to return the appropriate -/// backoff type for the standard Firebase App Check backend response codes. -- (GACAppCheckBackoffErrorHandler)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 GACAppCheckBackoffWrapper : NSObject - -/// Initializes the wrapper with `+[GACAppCheckBackoffWrapper currentDateProvider]`. -- (instancetype)init; - -- (instancetype)initWithDateProvider:(GACAppCheckDateProvider)dateProvider - NS_DESIGNATED_INITIALIZER; - -/// A date provider that returns `+[NSDate date]`. -+ (GACAppCheckDateProvider)currentDateProvider; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h b/AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h deleted file mode 100644 index ccc168f58395..000000000000 --- a/AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h +++ /dev/null @@ -1,56 +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 - -@class GACAppCheckHTTPError; - -NS_ASSUME_NONNULL_BEGIN - -void GACAppCheckSetErrorToPointer(NSError *error, NSError **pointer); - -@interface GACAppCheckErrorUtil : NSObject - -+ (NSError *)publicDomainErrorWithError:(NSError *)error; - -// MARK: - Internal errors - -+ (NSError *)cachedTokenNotFound; - -+ (NSError *)cachedTokenExpired; - -+ (NSError *)keychainErrorWithError:(NSError *)error; - -+ (GACAppCheckHTTPError *)APIErrorWithHTTPResponse:(NSHTTPURLResponse *)HTTPResponse - data:(nullable NSData *)data; - -+ (NSError *)APIErrorWithNetworkError:(NSError *)networkError; - -+ (NSError *)appCheckTokenResponseErrorWithMissingField:(NSString *)fieldName; - -+ (NSError *)appAttestAttestationResponseErrorWithMissingField:(NSString *)fieldName; - -+ (NSError *)JSONSerializationError:(NSError *)error; - -+ (NSError *)errorWithFailureReason:(NSString *)failureReason; - -+ (NSError *)unsupportedAttestationProvider:(NSString *)providerName; - -+ (NSError *)appAttestKeyIDNotFound; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.m b/AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.m deleted file mode 100644 index 34d9260b59cd..000000000000 --- a/AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.m +++ /dev/null @@ -1,146 +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 "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" - -#import - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h" - -@implementation GACAppCheckErrorUtil - -+ (NSError *)publicDomainErrorWithError:(NSError *)error { - if ([error.domain isEqualToString:GACAppCheckErrorDomain]) { - return error; - } - - return [self unknownErrorWithError:error]; -} - -#pragma mark - Internal errors - -+ (NSError *)cachedTokenNotFound { - NSString *failureReason = [NSString stringWithFormat:@"Cached token not found."]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeUnknown - failureReason:failureReason - underlyingError:nil]; -} - -+ (NSError *)cachedTokenExpired { - NSString *failureReason = [NSString stringWithFormat:@"Cached token expired."]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeUnknown - failureReason:failureReason - underlyingError:nil]; -} - -+ (NSError *)keychainErrorWithError:(NSError *)error { - if ([error.domain isEqualToString:kGULKeychainUtilsErrorDomain]) { - NSString *failureReason = [NSString stringWithFormat:@"Keychain access error."]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeKeychain - failureReason:failureReason - underlyingError:error]; - } - - return [self unknownErrorWithError:error]; -} - -+ (GACAppCheckHTTPError *)APIErrorWithHTTPResponse:(NSHTTPURLResponse *)HTTPResponse - data:(nullable NSData *)data { - return [[GACAppCheckHTTPError alloc] initWithHTTPResponse:HTTPResponse data:data]; -} - -+ (NSError *)APIErrorWithNetworkError:(NSError *)networkError { - NSString *failureReason = [NSString stringWithFormat:@"API request error."]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeServerUnreachable - failureReason:failureReason - underlyingError:networkError]; -} - -+ (NSError *)appCheckTokenResponseErrorWithMissingField:(NSString *)fieldName { - NSString *failureReason = [NSString - stringWithFormat:@"Unexpected app check token response format. Field `%@` is missing.", - fieldName]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeUnknown - failureReason:failureReason - underlyingError:nil]; -} - -+ (NSError *)appAttestAttestationResponseErrorWithMissingField:(NSString *)fieldName { - NSString *failureReason = - [NSString stringWithFormat:@"Unexpected attestation response format. Field `%@` is missing.", - fieldName]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeUnknown - failureReason:failureReason - underlyingError:nil]; -} - -+ (NSError *)JSONSerializationError:(NSError *)error { - NSString *failureReason = [NSString stringWithFormat:@"JSON serialization error."]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeUnknown - failureReason:failureReason - underlyingError:error]; -} - -+ (NSError *)unsupportedAttestationProvider:(NSString *)providerName { - NSString *failureReason = [NSString - stringWithFormat: - @"The attestation provider %@ is not supported on current platform and OS version.", - providerName]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeUnsupported - failureReason:failureReason - underlyingError:nil]; -} - -+ (NSError *)appAttestKeyIDNotFound { - NSString *failureReason = [NSString stringWithFormat:@"App attest key ID not found."]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeUnknown - failureReason:failureReason - underlyingError:nil]; -} - -+ (NSError *)errorWithFailureReason:(NSString *)failureReason { - return [self appCheckErrorWithCode:GACAppCheckErrorCodeUnknown - failureReason:failureReason - underlyingError:nil]; -} - -#pragma mark - Helpers - -+ (NSError *)unknownErrorWithError:(NSError *)error { - NSString *failureReason = error.userInfo[NSLocalizedFailureReasonErrorKey]; - return [self appCheckErrorWithCode:GACAppCheckErrorCodeUnknown - failureReason:failureReason - underlyingError:error]; -} - -+ (NSError *)appCheckErrorWithCode:(GACAppCheckErrorCode)code - failureReason:(nullable NSString *)failureReason - underlyingError:(nullable NSError *)underlyingError { - NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - userInfo[NSUnderlyingErrorKey] = underlyingError; - userInfo[NSLocalizedFailureReasonErrorKey] = failureReason; - - return [NSError errorWithDomain:GACAppCheckErrorDomain code:code userInfo:userInfo]; -} - -@end - -void GACAppCheckSetErrorToPointer(NSError *error, NSError **pointer) { - if (pointer != NULL) { - *pointer = error; - } -} diff --git a/AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h b/AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h deleted file mode 100644 index d37626c7a77a..000000000000 --- a/AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h +++ /dev/null @@ -1,32 +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 - -NS_ASSUME_NONNULL_BEGIN - -@interface GACAppCheckHTTPError : NSError - -@property(nonatomic, readonly) NSHTTPURLResponse *HTTPResponse; -@property(nonatomic, readonly, nonnull) NSData *data; - -- (instancetype)init NS_UNAVAILABLE; - -- (instancetype)initWithHTTPResponse:(NSHTTPURLResponse *)HTTPResponse data:(nullable NSData *)data; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.m b/AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.m deleted file mode 100644 index 4a2b20ea7b4c..000000000000 --- a/AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.m +++ /dev/null @@ -1,75 +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 "AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h" - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h" - -@implementation GACAppCheckHTTPError - -- (instancetype)initWithHTTPResponse:(NSHTTPURLResponse *)HTTPResponse - data:(nullable NSData *)data { - NSDictionary *userInfo = [[self class] userInfoWithHTTPResponse:HTTPResponse data:data]; - self = [super initWithDomain:GACAppCheckErrorDomain - code:GACAppCheckErrorCodeUnknown - userInfo:userInfo]; - if (self) { - _HTTPResponse = HTTPResponse; - _data = data; - } - return self; -} - -+ (NSDictionary *)userInfoWithHTTPResponse:(NSHTTPURLResponse *)HTTPResponse - data:(nullable NSData *)data { - NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSString *failureReason = - [NSString stringWithFormat:@"The server responded with an error: \n - URL: %@ \n - HTTP " - @"status code: %ld \n - Response body: %@", - HTTPResponse.URL, (long)HTTPResponse.statusCode, responseString]; - return @{NSLocalizedFailureReasonErrorKey : failureReason}; -} - -#pragma mark - NSCopying - -- (id)copyWithZone:(NSZone *)zone { - return [[[self class] alloc] initWithHTTPResponse:self.HTTPResponse data:self.data]; -} - -#pragma mark - NSSecureCoding - -- (nullable instancetype)initWithCoder:(NSCoder *)coder { - NSHTTPURLResponse *HTTPResponse = [coder decodeObjectOfClass:[NSHTTPURLResponse class] - forKey:@"HTTPResponse"]; - if (!HTTPResponse) { - return nil; - } - NSData *data = [coder decodeObjectOfClass:[NSData class] forKey:@"data"]; - - return [self initWithHTTPResponse:HTTPResponse data:data]; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:self.HTTPResponse forKey:@"HTTPResponse"]; - [coder encodeObject:self.data forKey:@"data"]; -} - -+ (BOOL)supportsSecureCoding { - return YES; -} - -@end diff --git a/AppCheckCore/Sources/Core/GACAppCheck.m b/AppCheckCore/Sources/Core/GACAppCheck.m deleted file mode 100644 index 903ce6d52f8a..000000000000 --- a/AppCheckCore/Sources/Core/GACAppCheck.m +++ /dev/null @@ -1,243 +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 "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheck.h" - -#if __has_include() -#import -#else -#import "FBLPromises.h" -#endif - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckProvider.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckTokenDelegate.h" - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h" -#import "AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.h" -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h" -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.h" - -NS_ASSUME_NONNULL_BEGIN - -static const NSTimeInterval kTokenExpirationThreshold = 5 * 60; // 5 min. - -static NSString *const kDummyFACTokenValue = @"eyJlcnJvciI6IlVOS05PV05fRVJST1IifQ=="; - -typedef void (^GACAppCheckTokenHandler)(id _Nullable token, - NSError *_Nullable error); - -@interface GACAppCheck () - -@property(nonatomic, readonly) NSString *serviceName; -@property(nonatomic, readonly) id appCheckProvider; -@property(nonatomic, readonly) id storage; -@property(nonatomic, readonly) id settings; -@property(nonatomic, readonly, nullable, weak) id tokenDelegate; - -@property(nonatomic, readonly, nullable) id tokenRefresher; - -@property(nonatomic, nullable) FBLPromise *ongoingRetrieveOrRefreshTokenPromise; - -@end - -@implementation GACAppCheck - -#pragma mark - Internal - -- (instancetype)initWithServiceName:(NSString *)serviceName - appCheckProvider:(id)appCheckProvider - storage:(id)storage - tokenRefresher:(id)tokenRefresher - settings:(id)settings - tokenDelegate:(nullable id)tokenDelegate { - self = [super init]; - if (self) { - _serviceName = serviceName; - _appCheckProvider = appCheckProvider; - _storage = storage; - _tokenRefresher = tokenRefresher; - _settings = settings; - _tokenDelegate = tokenDelegate; - - __auto_type __weak weakSelf = self; - tokenRefresher.tokenRefreshHandler = ^(GACAppCheckTokenRefreshCompletion _Nonnull completion) { - __auto_type strongSelf = weakSelf; - [strongSelf periodicTokenRefreshWithCompletion:completion]; - }; - } - return self; -} - -#pragma mark - Public - -- (instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - appCheckProvider:(id)appCheckProvider - settings:(id)settings - tokenDelegate:(nullable id)tokenDelegate - keychainAccessGroup:(nullable NSString *)accessGroup { - GACAppCheckTokenRefreshResult *refreshResult = - [[GACAppCheckTokenRefreshResult alloc] initWithStatusNever]; - GACAppCheckTokenRefresher *tokenRefresher = - [[GACAppCheckTokenRefresher alloc] initWithRefreshResult:refreshResult settings:settings]; - - NSString *tokenKey = - [NSString stringWithFormat:@"app_check_token.%@.%@", serviceName, resourceName]; - GACAppCheckStorage *storage = [[GACAppCheckStorage alloc] initWithTokenKey:tokenKey - accessGroup:accessGroup]; - - return [self initWithServiceName:serviceName - appCheckProvider:appCheckProvider - storage:storage - tokenRefresher:tokenRefresher - settings:settings - tokenDelegate:tokenDelegate]; -} - -#pragma mark - GACAppCheckInterop - -- (void)getTokenForcingRefresh:(BOOL)forcingRefresh completion:(GACAppCheckTokenHandler)handler { - [self retrieveOrRefreshTokenForcingRefresh:forcingRefresh] - .then(^id _Nullable(id token) { - handler(token, nil); - return token; - }) - .catch(^(NSError *_Nonnull error) { - handler(nil, error); - }); -} - -- (void)getLimitedUseTokenWithCompletion:(GACAppCheckTokenHandler)handler { - [self limitedUseToken] - .then(^id _Nullable(id token) { - handler(token, nil); - return token; - }) - .catch(^(NSError *_Nonnull error) { - handler(nil, error); - }); -} - -#pragma mark - FAA token cache - -- (FBLPromise *)retrieveOrRefreshTokenForcingRefresh:(BOOL)forcingRefresh { - return [FBLPromise do:^id _Nullable { - if (self.ongoingRetrieveOrRefreshTokenPromise == nil) { - // Kick off a new operation only when there is not an ongoing one. - self.ongoingRetrieveOrRefreshTokenPromise = - [self createRetrieveOrRefreshTokenPromiseForcingRefresh:forcingRefresh] - - // Release the ongoing operation promise on completion. - .then(^GACAppCheckToken *(GACAppCheckToken *token) { - self.ongoingRetrieveOrRefreshTokenPromise = nil; - return token; - }) - .recover(^NSError *(NSError *error) { - self.ongoingRetrieveOrRefreshTokenPromise = nil; - return error; - }); - } - return self.ongoingRetrieveOrRefreshTokenPromise; - }]; -} - -- (FBLPromise *)createRetrieveOrRefreshTokenPromiseForcingRefresh: - (BOOL)forcingRefresh { - return [self getCachedValidTokenForcingRefresh:forcingRefresh].recover( - ^id _Nullable(NSError *_Nonnull error) { - return [self refreshToken]; - }); -} - -- (FBLPromise *)getCachedValidTokenForcingRefresh:(BOOL)forcingRefresh { - if (forcingRefresh) { - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise reject:[GACAppCheckErrorUtil cachedTokenNotFound]]; - return rejectedPromise; - } - - return [self.storage getToken].then(^id(GACAppCheckToken *_Nullable token) { - if (token == nil) { - return [GACAppCheckErrorUtil cachedTokenNotFound]; - } - - BOOL isTokenExpiredOrExpiresSoon = - [token.expirationDate timeIntervalSinceNow] < kTokenExpirationThreshold; - if (isTokenExpiredOrExpiresSoon) { - return [GACAppCheckErrorUtil cachedTokenExpired]; - } - - return token; - }); -} - -- (FBLPromise *)refreshToken { - return [FBLPromise - wrapObjectOrErrorCompletion:^(FBLPromiseObjectOrErrorCompletion _Nonnull handler) { - [self.appCheckProvider getTokenWithCompletion:handler]; - }] - .then(^id _Nullable(GACAppCheckToken *_Nullable token) { - return [self.storage setToken:token]; - }) - .then(^id _Nullable(GACAppCheckToken *_Nullable token) { - // TODO: Make sure the self.tokenRefresher is updated only once. Currently the timer will be - // updated twice in the case when the refresh triggered by self.tokenRefresher, but it - // should be fine for now as it is a relatively cheap operation. - __auto_type refreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:token.expirationDate - receivedAtDate:token.receivedAtDate]; - [self.tokenRefresher updateWithRefreshResult:refreshResult]; - if (self.tokenDelegate) { - [self.tokenDelegate tokenDidUpdate:token serviceName:self.serviceName]; - } - return token; - }); -} - -- (FBLPromise *)limitedUseToken { - return - [FBLPromise wrapObjectOrErrorCompletion:^( - FBLPromiseObjectOrErrorCompletion _Nonnull handler) { - [self.appCheckProvider getTokenWithCompletion:handler]; - }].then(^id _Nullable(GACAppCheckToken *_Nullable token) { - return token; - }); -} - -#pragma mark - Token auto refresh - -- (void)periodicTokenRefreshWithCompletion:(GACAppCheckTokenRefreshCompletion)completion { - [self retrieveOrRefreshTokenForcingRefresh:NO] - .then(^id _Nullable(GACAppCheckToken *_Nullable token) { - __auto_type refreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:token.expirationDate - receivedAtDate:token.receivedAtDate]; - completion(refreshResult); - return nil; - }) - .catch(^(NSError *error) { - __auto_type refreshResult = [[GACAppCheckTokenRefreshResult alloc] initWithStatusFailure]; - completion(refreshResult); - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h b/AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h deleted file mode 100644 index 0b71c3e34dcb..000000000000 --- a/AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h +++ /dev/null @@ -1,50 +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 - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckLogger.h" - -NS_ASSUME_NONNULL_BEGIN - -/** Prints the given code and message to the console. - * - * @param code The message code describing the nature of the log. - * @param logLevel The log level of this log. - * @param message The message string to log. - */ -FOUNDATION_EXPORT -void GACAppCheckLog(GACAppCheckMessageCode code, - GACAppCheckLogLevel logLevel, - NSString *_Nonnull message); - -#define GACAppCheckLogFault(MESSAGE_CODE, MESSAGE) \ - GACAppCheckLog(MESSAGE_CODE, GACAppCheckLogLevelFault, MESSAGE); - -#define GACAppCheckLogError(MESSAGE_CODE, MESSAGE) \ - GACAppCheckLog(MESSAGE_CODE, GACAppCheckLogLevelError, MESSAGE); - -#define GACAppCheckLogWarning(MESSAGE_CODE, MESSAGE) \ - GACAppCheckLog(MESSAGE_CODE, GACAppCheckLogLevelWarning, MESSAGE); - -#define GACAppCheckLogInfo(MESSAGE_CODE, MESSAGE) \ - GACAppCheckLog(MESSAGE_CODE, GACAppCheckLogLevelInfo, MESSAGE); - -#define GACAppCheckLogDebug(MESSAGE_CODE, MESSAGE) \ - GACAppCheckLog(MESSAGE_CODE, GACAppCheckLogLevelDebug, MESSAGE); - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/GACAppCheckLogger.m b/AppCheckCore/Sources/Core/GACAppCheckLogger.m deleted file mode 100644 index 9e9e39709b1e..000000000000 --- a/AppCheckCore/Sources/Core/GACAppCheckLogger.m +++ /dev/null @@ -1,82 +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 "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckLogger.h" - -#import "AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h" - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - Public - -@implementation GACAppCheckLogger - -// Note: Declared as volatile to make getting and setting atomic. -static volatile GACAppCheckLogLevel _logLevel; - -+ (GACAppCheckLogLevel)logLevel { - return _logLevel; -} - -+ (void)setLogLevel:(GACAppCheckLogLevel)logLevel { - _logLevel = logLevel; -} - -@end - -#pragma mark - Helpers - -NSString *GACAppCheckMessageCodeEnumToString(GACAppCheckMessageCode code) { - return [[NSString alloc] initWithFormat:@"I-GAC%06ld", (long)code]; -} - -NSString *GACAppCheckLoggerLevelEnumToString(GACAppCheckLogLevel logLevel) { - switch (logLevel) { - case GACAppCheckLogLevelFault: - return @"Fault"; - case GACAppCheckLogLevelError: - return @"Error"; - case GACAppCheckLogLevelWarning: - return @"Warning"; - case GACAppCheckLogLevelInfo: - return @"Info"; - case GACAppCheckLogLevelDebug: - return @"Debug"; - } -} - -#pragma mark - Logging Functions - -/** - * Generates the logging functions using macros. - * - * Calling GACLogError(@"Firebase", @"I-GAC000001", @"Configure %@ failed.", @"blah") shows: - * yyyy-mm-dd hh:mm:ss.SSS sender[PID] [Firebase/AppCheck][I-GAC000001] Configure blah - * failed. Calling GACLogDebug(@"GoogleSignIn", @"I-GAC000002", @"Configure succeed.") shows: - * yyyy-mm-dd hh:mm:ss.SSS sender[PID] [GoogleSignIn/AppCheck][I-COR000002] Configure - * succeed. - */ -void GACAppCheckLog(GACAppCheckMessageCode code, GACAppCheckLogLevel logLevel, NSString *message) { - // Don't log anything in not debug builds. -#if !NDEBUG - if (logLevel >= GACAppCheckLogger.logLevel) { - NSLog(@"<%@> [AppCheckCore][%@] %@", GACAppCheckLoggerLevelEnumToString(logLevel), - GACAppCheckMessageCodeEnumToString(code), message); - } -#endif // !NDEBUG -} - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/GACAppCheckSettings.m b/AppCheckCore/Sources/Core/GACAppCheckSettings.m deleted file mode 100644 index e533587bd874..000000000000 --- a/AppCheckCore/Sources/Core/GACAppCheckSettings.m +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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. - */ - -<<<<<<<< HEAD:AppCheckCore/Sources/Core/GACAppCheckSettings.m -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h" -======== -#import "AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.h" ->>>>>>>> e6cc60fb4 (Renamed `AppCheck` to `AppCheckCore` (#11581)):AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.m - -NS_ASSUME_NONNULL_BEGIN - -@implementation GACAppCheckSettings - -@synthesize isTokenAutoRefreshEnabled = _isTokenAutoRefreshEnabled; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/GACAppCheckToken.m b/AppCheckCore/Sources/Core/GACAppCheckToken.m deleted file mode 100644 index c016a3f55a11..000000000000 --- a/AppCheckCore/Sources/Core/GACAppCheckToken.m +++ /dev/null @@ -1,45 +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 "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation GACAppCheckToken - -@synthesize token = _token; -@synthesize expirationDate = _expirationDate; -@synthesize receivedAtDate = _receivedAtDate; - -- (instancetype)initWithToken:(NSString *)token - expirationDate:(NSDate *)expirationDate - receivedAtDate:(NSDate *)receivedAtDate { - self = [super init]; - if (self) { - _token = [token copy]; - _expirationDate = expirationDate; - _receivedAtDate = receivedAtDate; - } - return self; -} - -- (instancetype)initWithToken:(NSString *)token expirationDate:(NSDate *)expirationDate { - return [self initWithToken:token expirationDate:expirationDate receivedAtDate:[NSDate date]]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.h b/AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.h deleted file mode 100644 index 0aa3112e18ee..000000000000 --- a/AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.h +++ /dev/null @@ -1,64 +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 - -@class GACAppCheckToken; -@class FBLPromise; -@class GULKeychainStorage; - -NS_ASSUME_NONNULL_BEGIN - -@protocol GACAppCheckStorageProtocol - -/** Manages storage of the FAA token. - * @param token A token object to store or `nil` to remove existing token. - * @return A promise that is resolved with the stored object in the case of success or is rejected - * with a specific error otherwise. - */ -- (FBLPromise *)setToken:(nullable GACAppCheckToken *)token; - -/** Reads a stored FAA token. - * @return A promise that is resolved with a stored token or `nil` if there is not a stored token. - * The promise is rejected with an error in the case of a failure. - */ -- (FBLPromise *)getToken; - -@end - -/// The class provides an implementation of persistent storage to store data like FAA token, etc. -@interface GACAppCheckStorage : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -/** Default convenience initializer. - * @param tokenKey The key to store the token for the storage instance. - * @param accessGroup The Keychain Access Group. - */ -- (instancetype)initWithTokenKey:(NSString *)tokenKey accessGroup:(nullable NSString *)accessGroup; - -/** Designated initializer. - * @param tokenKey The key to store the token for the storage instance. - * @param keychainStorage An instance of `GULKeychainStorage` used as an underlying secure storage. - * @param accessGroup The Keychain Access Group. - */ -- (instancetype)initWithTokenKey:(NSString *)tokenKey - keychainStorage:(GULKeychainStorage *)keychainStorage - accessGroup:(nullable NSString *)accessGroup NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.m b/AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.m deleted file mode 100644 index cae8c65ffa8c..000000000000 --- a/AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.m +++ /dev/null @@ -1,109 +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 "AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.h" - -#if __has_include() -#import -#else -#import "FBLPromises.h" -#endif - -#import - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.h" - -NS_ASSUME_NONNULL_BEGIN - -static NSString *const kKeychainService = @"com.google.app_check_core.token_storage"; - -@interface GACAppCheckStorage () - -@property(nonatomic, readonly) NSString *tokenKey; -@property(nonatomic, readonly) GULKeychainStorage *keychainStorage; -@property(nonatomic, readonly, nullable) NSString *accessGroup; - -@end - -@implementation GACAppCheckStorage - -- (instancetype)initWithTokenKey:(NSString *)tokenKey - keychainStorage:(GULKeychainStorage *)keychainStorage - accessGroup:(nullable NSString *)accessGroup { - self = [super init]; - if (self) { - _tokenKey = [tokenKey copy]; - _keychainStorage = keychainStorage; - _accessGroup = [accessGroup copy]; - } - return self; -} - -- (instancetype)initWithTokenKey:(NSString *)tokenKey accessGroup:(nullable NSString *)accessGroup { - GULKeychainStorage *keychainStorage = - [[GULKeychainStorage alloc] initWithService:kKeychainService]; - return [self initWithTokenKey:tokenKey keychainStorage:keychainStorage accessGroup:accessGroup]; -} - -- (FBLPromise *)getToken { - return [self.keychainStorage getObjectForKey:[self tokenKey] - objectClass:[GACAppCheckStoredToken class] - accessGroup:self.accessGroup] - .then(^GACAppCheckToken *(id storedToken) { - if ([(NSObject *)storedToken isKindOfClass:[GACAppCheckStoredToken class]]) { - return [(GACAppCheckStoredToken *)storedToken appCheckToken]; - } else { - return nil; - } - }) - .recover(^NSError *(NSError *error) { - return [GACAppCheckErrorUtil keychainErrorWithError:error]; - }); -} - -- (FBLPromise *)setToken:(nullable GACAppCheckToken *)token { - if (token) { - return [self storeToken:token].recover(^NSError *(NSError *error) { - return [GACAppCheckErrorUtil keychainErrorWithError:error]; - }); - } else { - return [self.keychainStorage removeObjectForKey:[self tokenKey] accessGroup:self.accessGroup] - .then(^id _Nullable(NSNull *_Nullable value) { - return token; - }) - .recover(^NSError *(NSError *error) { - return [GACAppCheckErrorUtil keychainErrorWithError:error]; - }); - } -} - -#pragma mark - Helpers - -- (FBLPromise *)storeToken:(nullable GACAppCheckToken *)token { - GACAppCheckStoredToken *storedToken = [[GACAppCheckStoredToken alloc] init]; - [storedToken updateWithToken:token]; - return [self.keychainStorage setObject:storedToken - forKey:[self tokenKey] - accessGroup:self.accessGroup] - .then(^id _Nullable(NSNull *_Nullable value) { - return token; - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.h b/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.h deleted file mode 100644 index a0bb67d2fb1f..000000000000 --- a/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.h +++ /dev/null @@ -1,31 +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 "AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.h" - -@class GACAppCheckToken; - -NS_ASSUME_NONNULL_BEGIN - -@interface GACAppCheckStoredToken (GACAppCheckToken) - -- (void)updateWithToken:(GACAppCheckToken *)token; - -- (GACAppCheckToken *)appCheckToken; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.m b/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.m deleted file mode 100644 index af8c0a05e563..000000000000 --- a/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.m +++ /dev/null @@ -1,35 +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 "AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.h" - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -@implementation GACAppCheckStoredToken (GACAppCheckToken) - -- (void)updateWithToken:(GACAppCheckToken *)token { - self.token = token.token; - self.expirationDate = token.expirationDate; - self.receivedAtDate = token.receivedAtDate; -} - -- (GACAppCheckToken *)appCheckToken { - return [[GACAppCheckToken alloc] initWithToken:self.token - expirationDate:self.expirationDate - receivedAtDate:self.receivedAtDate]; -} - -@end diff --git a/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.h b/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.h deleted file mode 100644 index ba17a8a40c87..000000000000 --- a/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.h +++ /dev/null @@ -1,37 +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 - -NS_ASSUME_NONNULL_BEGIN - -@interface GACAppCheckStoredToken : NSObject - -/// The Firebase App Check token. -@property(nonatomic, copy, nullable) NSString *token; - -/// The Firebase App Check token expiration date in the device local time. -@property(nonatomic, strong, nullable) NSDate *expirationDate; - -/// The date when the Firebase App Check token was received in the device's local time. -@property(nonatomic, strong, nullable) NSDate *receivedAtDate; - -/// The version of local storage. -@property(nonatomic, readonly) NSInteger storageVersion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.m b/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.m deleted file mode 100644 index e79a125136bb..000000000000 --- a/AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.m +++ /dev/null @@ -1,62 +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 "AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.h" - -static NSString *const kTokenKey = @"token"; -static NSString *const kExpirationDateKey = @"expirationDate"; -static NSString *const kReceivedAtDateKey = @"receivedAtDate"; -static NSString *const kStorageVersionKey = @"storageVersion"; - -static const NSInteger kStorageVersion = 2; - -NS_ASSUME_NONNULL_BEGIN - -@implementation GACAppCheckStoredToken - -- (NSInteger)storageVersion { - return kStorageVersion; -} - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:self.token forKey:kTokenKey]; - [coder encodeObject:self.expirationDate forKey:kExpirationDateKey]; - [coder encodeObject:self.receivedAtDate forKey:kReceivedAtDateKey]; - [coder encodeInteger:self.storageVersion forKey:kStorageVersionKey]; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder { - self = [super init]; - if (self) { - NSInteger decodedStorageVersion = [coder decodeIntegerForKey:kStorageVersionKey]; - if (decodedStorageVersion > kStorageVersion) { - // TODO: Log a message. - } - - _token = [coder decodeObjectOfClass:[NSString class] forKey:kTokenKey]; - _expirationDate = [coder decodeObjectOfClass:[NSDate class] forKey:kExpirationDateKey]; - _receivedAtDate = [coder decodeObjectOfClass:[NSDate class] forKey:kReceivedAtDateKey]; - } - return self; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.h b/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.h deleted file mode 100644 index a6302a8a2561..000000000000 --- a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.h +++ /dev/null @@ -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 - -NS_ASSUME_NONNULL_BEGIN - -@protocol GACAppCheckTimerProtocol - -- (void)invalidate; - -@end - -typedef id _Nullable (^GACTimerProvider)(NSDate *fireDate, - dispatch_queue_t queue, - dispatch_block_t handler); - -@interface GACAppCheckTimer : NSObject - -+ (GACTimerProvider)timerProvider; - -- (nullable instancetype)initWithFireDate:(NSDate *)date - dispatchQueue:(dispatch_queue_t)dispatchQueue - block:(dispatch_block_t)block; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.m b/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.m deleted file mode 100644 index 8c2b0cf3fa01..000000000000 --- a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.m +++ /dev/null @@ -1,91 +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 "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface GACAppCheckTimer () -@property(nonatomic, readonly) dispatch_queue_t dispatchQueue; -@property(atomic, readonly) dispatch_source_t timer; -@end - -@implementation GACAppCheckTimer - -+ (GACTimerProvider)timerProvider { - return ^id _Nullable(NSDate *fireDate, dispatch_queue_t queue, - dispatch_block_t handler) { - return [[GACAppCheckTimer alloc] initWithFireDate:fireDate dispatchQueue:queue block:handler]; - }; -} - -+ (nullable instancetype)timerFireDate:(NSDate *)fireDate - dispatchQueue:(dispatch_queue_t)dispatchQueue - block:(dispatch_block_t)block { - return [[GACAppCheckTimer alloc] initWithFireDate:fireDate - dispatchQueue:dispatchQueue - block:block]; -} - -- (nullable instancetype)initWithFireDate:(NSDate *)date - dispatchQueue:(dispatch_queue_t)dispatchQueue - block:(dispatch_block_t)block { - self = [super init]; - if (self == nil) { - return nil; - } - - if (block == nil) { - return nil; - } - - NSTimeInterval scheduleInSec = [date timeIntervalSinceNow]; - if (scheduleInSec <= 0) { - return nil; - } - - dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, scheduleInSec * NSEC_PER_SEC); - _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.dispatchQueue); - dispatch_source_set_timer(_timer, startTime, UINT64_MAX * NSEC_PER_SEC, 0); - - __auto_type __weak weakSelf = self; - dispatch_source_set_event_handler(_timer, ^{ - __auto_type strongSelf = weakSelf; - - // The initializer returns a one-off timer, so we need to invalidate the dispatch timer to - // prevent firing again. - [strongSelf invalidate]; - block(); - }); - - dispatch_resume(_timer); - - return self; -} - -- (void)dealloc { - [self invalidate]; -} - -- (void)invalidate { - if (self.timer != nil) { - dispatch_source_cancel(self.timer); - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h b/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h deleted file mode 100644 index 6cfc875f2980..000000000000 --- a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h +++ /dev/null @@ -1,63 +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 - -NS_ASSUME_NONNULL_BEGIN - -/// Represents possible results of a Firebase App Check token refresh attempt that matter for -/// `GACAppCheckTokenRefresher`. -typedef NS_ENUM(NSInteger, GACAppCheckTokenRefreshStatus) { - // The token has not been refreshed. - GACAppCheckTokenRefreshStatusNever, - - // The token was successfully refreshed. - GACAppCheckTokenRefreshStatusSuccess, - - // The token refresh failed. - GACAppCheckTokenRefreshStatusFailure -}; - -/// An object to pass the possible results of a Firebase App Check token refresh attempt and -/// supplementary data. -@interface GACAppCheckTokenRefreshResult : NSObject - -/// Status of the refresh. -@property(nonatomic, readonly) GACAppCheckTokenRefreshStatus status; - -/// A date when the new Firebase App Check token is expiring. -@property(nonatomic, readonly, nullable) NSDate *tokenExpirationDate; - -/// A date when the new Firebase App Check token was received from the server. -@property(nonatomic, readonly, nullable) NSDate *tokenReceivedAtDate; - -- (instancetype)init NS_UNAVAILABLE; - -/// Initializes the instance with `GACAppCheckTokenRefreshStatusNever`. -- (instancetype)initWithStatusNever; - -/// Initializes the instance with `GACAppCheckTokenRefreshStatusFailure`. -- (instancetype)initWithStatusFailure; - -/// Initializes the instance with `GACAppCheckTokenRefreshStatusSuccess`. -/// @param tokenExpirationDate See `tokenExpirationDate` property. -/// @param tokenReceivedAtDate See `tokenReceivedAtDate` property. -- (instancetype)initWithStatusSuccessAndExpirationDate:(NSDate *)tokenExpirationDate - receivedAtDate:(NSDate *)tokenReceivedAtDate; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.m b/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.m deleted file mode 100644 index 33ad19e569ea..000000000000 --- a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.m +++ /dev/null @@ -1,64 +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 "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface GACAppCheckTokenRefreshResult () - -- (instancetype)initWithStatus:(GACAppCheckTokenRefreshStatus)status - expirationDate:(nullable NSDate *)tokenExpirationDate - receivedAtDate:(nullable NSDate *)tokenReceivedAtDate NS_DESIGNATED_INITIALIZER; - -@end - -@implementation GACAppCheckTokenRefreshResult - -- (instancetype)initWithStatus:(GACAppCheckTokenRefreshStatus)status - expirationDate:(nullable NSDate *)tokenExpirationDate - receivedAtDate:(nullable NSDate *)tokenReceivedAtDate { - self = [super init]; - if (self) { - _status = status; - _tokenExpirationDate = tokenExpirationDate; - _tokenReceivedAtDate = tokenReceivedAtDate; - } - return self; -} - -- (instancetype)initWithStatusNever { - return [self initWithStatus:GACAppCheckTokenRefreshStatusNever - expirationDate:nil - receivedAtDate:nil]; -} - -- (instancetype)initWithStatusFailure { - return [self initWithStatus:GACAppCheckTokenRefreshStatusFailure - expirationDate:nil - receivedAtDate:nil]; -} - -- (instancetype)initWithStatusSuccessAndExpirationDate:(NSDate *)tokenExpirationDate - receivedAtDate:(NSDate *)tokenReceivedAtDate { - return [self initWithStatus:GACAppCheckTokenRefreshStatusSuccess - expirationDate:tokenExpirationDate - receivedAtDate:tokenReceivedAtDate]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.h b/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.h deleted file mode 100644 index dc6bf219173e..000000000000 --- a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.h +++ /dev/null @@ -1,69 +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 - -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.h" - -@protocol GACAppCheckSettingsProtocol; -@class GACAppCheckTokenRefreshResult; - -NS_ASSUME_NONNULL_BEGIN - -/** The block to be called on the token refresh completion. - * @param refreshResult The refresh result. - */ -typedef void (^GACAppCheckTokenRefreshCompletion)(GACAppCheckTokenRefreshResult *refreshResult); - -/** The block that will be called by `GACAppCheckTokenRefresher` to trigger the token refresh. - * @param completion The block that the client must call when the token refresh was completed. - */ -typedef void (^GACAppCheckTokenRefreshBlock)(GACAppCheckTokenRefreshCompletion completion); - -@protocol GACAppCheckTokenRefresherProtocol - -/// The block to be called when refresh is needed. The client is responsible for actual token -/// refresh in the block. -@property(nonatomic, copy) GACAppCheckTokenRefreshBlock tokenRefreshHandler; - -/// Updates the next refresh date based on the new token expiration date. This method should be -/// called when the token update was initiated not by the refresher. -/// @param refreshResult A result of a refresh attempt. -- (void)updateWithRefreshResult:(GACAppCheckTokenRefreshResult *)refreshResult; - -@end - -/// The class calls `tokenRefreshHandler` periodically to keep FAC token fresh to reduce FAC token -/// exchange overhead for product requests. -@interface GACAppCheckTokenRefresher : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -/// The designated initializer. -/// @param refreshResult A previous token refresh attempt result. -/// @param settings An object that handles Firebase app check settings. -- (instancetype)initWithRefreshResult:(GACAppCheckTokenRefreshResult *)refreshResult - timerProvider:(GACTimerProvider)timerProvider - settings:(id)settings - NS_DESIGNATED_INITIALIZER; - -/// A convenience initializer with a timer provider returning an instance of `GACAppCheckTimer`. -- (instancetype)initWithRefreshResult:(GACAppCheckTokenRefreshResult *)refreshResult - settings:(id)settings; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.m b/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.m deleted file mode 100644 index 084d93eb203d..000000000000 --- a/AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.m +++ /dev/null @@ -1,216 +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 "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.h" - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h" - -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.h" -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h" - -NS_ASSUME_NONNULL_BEGIN - -static const NSTimeInterval kInitialBackoffTimeInterval = 30; -static const NSTimeInterval kMaximumBackoffTimeInterval = 16 * 60; - -static const NSTimeInterval kMinimumAutoRefreshTimeInterval = 60; // 1 min. - -/// How much time in advance to auto-refresh token before it's expiration. E.g. 0.5 means that the -/// token will be refreshed half way through it's intended time to live. -static const double kAutoRefreshFraction = 0.5; - -@interface GACAppCheckTokenRefresher () - -@property(nonatomic, readonly) dispatch_queue_t refreshQueue; - -@property(nonatomic, readonly) id settings; - -@property(nonatomic, readonly) GACTimerProvider timerProvider; -@property(atomic, nullable) id timer; -@property(atomic) NSUInteger retryCount; - -/// Initial refresh result to be used when `tokenRefreshHandler` has been sent. -@property(nonatomic, nullable) GACAppCheckTokenRefreshResult *initialRefreshResult; - -@end - -@implementation GACAppCheckTokenRefresher - -@synthesize tokenRefreshHandler = _tokenRefreshHandler; - -- (instancetype)initWithRefreshResult:(GACAppCheckTokenRefreshResult *)refreshResult - timerProvider:(GACTimerProvider)timerProvider - settings:(id)settings { - self = [super init]; - if (self) { - _refreshQueue = - dispatch_queue_create("com.firebase.GACAppCheckTokenRefresher", DISPATCH_QUEUE_SERIAL); - _initialRefreshResult = refreshResult; - _timerProvider = timerProvider; - _settings = settings; - } - return self; -} - -- (instancetype)initWithRefreshResult:(GACAppCheckTokenRefreshResult *)refreshResult - settings:(id)settings { - return [self initWithRefreshResult:refreshResult - timerProvider:[GACAppCheckTimer timerProvider] - settings:settings]; -} - -- (void)dealloc { - [self cancelTimer]; -} - -- (void)setTokenRefreshHandler:(GACAppCheckTokenRefreshBlock)tokenRefreshHandler { - @synchronized(self) { - _tokenRefreshHandler = tokenRefreshHandler; - - // Check if handler is being set for the first time and if yes then schedule first refresh. - if (tokenRefreshHandler && self.initialRefreshResult) { - GACAppCheckTokenRefreshResult *initialTokenRefreshResult = self.initialRefreshResult; - self.initialRefreshResult = nil; - [self scheduleWithTokenRefreshResult:initialTokenRefreshResult]; - } - } -} - -- (GACAppCheckTokenRefreshBlock)tokenRefreshHandler { - @synchronized(self) { - return _tokenRefreshHandler; - } -} - -- (void)updateWithRefreshResult:(GACAppCheckTokenRefreshResult *)refreshResult { - switch (refreshResult.status) { - case GACAppCheckTokenRefreshStatusNever: - case GACAppCheckTokenRefreshStatusSuccess: - self.retryCount = 0; - break; - - case GACAppCheckTokenRefreshStatusFailure: - self.retryCount += 1; - break; - } - - [self scheduleWithTokenRefreshResult:refreshResult]; -} - -- (void)refresh { - if (self.tokenRefreshHandler == nil) { - return; - } - - if (!self.settings.isTokenAutoRefreshEnabled) { - return; - } - - __auto_type __weak weakSelf = self; - self.tokenRefreshHandler(^(GACAppCheckTokenRefreshResult *refreshResult) { - __auto_type strongSelf = weakSelf; - [strongSelf updateWithRefreshResult:refreshResult]; - }); -} - -- (void)scheduleWithTokenRefreshResult:(GACAppCheckTokenRefreshResult *)refreshResult { - // Schedule the refresh only when allowed. - if (self.settings.isTokenAutoRefreshEnabled) { - NSDate *refreshDate = [self nextRefreshDateWithTokenRefreshResult:refreshResult]; - [self scheduleRefreshAtDate:refreshDate]; - } -} - -- (void)scheduleRefreshAtDate:(NSDate *)refreshDate { - [self cancelTimer]; - - NSTimeInterval scheduleInSec = [refreshDate timeIntervalSinceNow]; - - __auto_type __weak weakSelf = self; - dispatch_block_t refreshHandler = ^{ - __auto_type strongSelf = weakSelf; - [strongSelf refresh]; - }; - - // Refresh straight away if the refresh time is too close. - if (scheduleInSec <= 0) { - dispatch_async(self.refreshQueue, refreshHandler); - return; - } - - self.timer = self.timerProvider(refreshDate, self.refreshQueue, refreshHandler); -} - -- (void)cancelTimer { - [self.timer invalidate]; -} - -- (NSDate *)nextRefreshDateWithTokenRefreshResult:(GACAppCheckTokenRefreshResult *)refreshResult { - switch (refreshResult.status) { - case GACAppCheckTokenRefreshStatusSuccess: { - NSTimeInterval timeToLive = [refreshResult.tokenExpirationDate - timeIntervalSinceDate:refreshResult.tokenReceivedAtDate]; - timeToLive = MAX(timeToLive, 0); - - // Refresh in 50% of TTL + 5 min. - NSTimeInterval targetRefreshSinceReceivedDate = timeToLive * kAutoRefreshFraction + 5 * 60; - NSDate *targetRefreshDate = [refreshResult.tokenReceivedAtDate - dateByAddingTimeInterval:targetRefreshSinceReceivedDate]; - - // Don't schedule later than expiration date. - NSDate *refreshDate = [targetRefreshDate earlierDate:refreshResult.tokenExpirationDate]; - - // Don't schedule a refresh earlier than in 1 min from now. - if ([refreshDate timeIntervalSinceNow] < kMinimumAutoRefreshTimeInterval) { - refreshDate = [NSDate dateWithTimeIntervalSinceNow:kMinimumAutoRefreshTimeInterval]; - } - return refreshDate; - } break; - - case GACAppCheckTokenRefreshStatusFailure: { - // Repeat refresh attempt later. - NSTimeInterval backoffTime = [[self class] backoffTimeForRetryCount:self.retryCount]; - return [NSDate dateWithTimeIntervalSinceNow:backoffTime]; - } break; - - case GACAppCheckTokenRefreshStatusNever: - // Refresh ASAP. - return [NSDate date]; - break; - } -} - -#pragma mark - Backoff - -+ (NSTimeInterval)backoffTimeForRetryCount:(NSInteger)retryCount { - if (retryCount == 0) { - // No backoff for the first attempt. - return 0; - } - - NSTimeInterval exponentialInterval = - kInitialBackoffTimeInterval * pow(2, retryCount - 1) + [self randomMilliseconds]; - return MIN(exponentialInterval, kMaximumBackoffTimeInterval); -} - -+ (NSTimeInterval)randomMilliseconds { - int32_t random_millis = ABS(arc4random() % 1000); - return (double)random_millis * 0.001; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Core/Utils/GACAppCheckCryptoUtils.h b/AppCheckCore/Sources/Core/Utils/GACAppCheckCryptoUtils.h deleted file mode 100644 index 839dcc1fd695..000000000000 --- a/AppCheckCore/Sources/Core/Utils/GACAppCheckCryptoUtils.h +++ /dev/null @@ -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 - -NS_ASSUME_NONNULL_BEGIN - -@interface GACAppCheckCryptoUtils : NSObject - -+ (NSData *)sha256HashFromData:(NSData *)dataToHash; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h b/AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h deleted file mode 100644 index 72752406e19f..000000000000 --- a/AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h +++ /dev/null @@ -1,45 +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 - -@class FBLPromise; -@class GACAppCheckToken; -@protocol GACAppCheckAPIServiceProtocol; - -NS_ASSUME_NONNULL_BEGIN - -@protocol GACAppCheckDebugProviderAPIServiceProtocol - -- (FBLPromise *)appCheckTokenWithDebugToken:(NSString *)debugToken; - -@end - -@interface GACAppCheckDebugProviderAPIService - : NSObject - -/// Default initializer. -/// @param APIService An instance implementing `GACAppCheckAPIServiceProtocol` to be used to send -/// network requests to the App Check backend. -/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is -/// "projects/{project_id}/apps/{app_id}". See https://google.aip.dev/122 for more details about -/// resource names. -- (instancetype)initWithAPIService:(id)APIService - resourceName:(NSString *)resourceName; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.m b/AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.m deleted file mode 100644 index cdf3f9f1d93b..000000000000 --- a/AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.m +++ /dev/null @@ -1,111 +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 "AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h" - -#if __has_include() -#import -#else -#import "FBLPromises.h" -#endif - -#import -#import - -#import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h" -#import "AppCheckCore/Sources/Core/APIService/GACAppCheckToken+APIResponse.h" - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h" - -NS_ASSUME_NONNULL_BEGIN - -static NSString *const kContentTypeKey = @"Content-Type"; -static NSString *const kJSONContentType = @"application/json"; -static NSString *const kDebugTokenField = @"debug_token"; - -@interface GACAppCheckDebugProviderAPIService () - -@property(nonatomic, readonly) id APIService; - -@property(nonatomic, readonly) NSString *resourceName; - -@end - -@implementation GACAppCheckDebugProviderAPIService - -- (instancetype)initWithAPIService:(id)APIService - resourceName:(NSString *)resourceName { - self = [super init]; - if (self) { - _APIService = APIService; - _resourceName = resourceName; - } - return self; -} - -#pragma mark - Public API - -- (FBLPromise *)appCheckTokenWithDebugToken:(NSString *)debugToken { - NSString *URLString = [NSString - stringWithFormat:@"%@/%@:exchangeDebugToken", self.APIService.baseURL, self.resourceName]; - NSURL *URL = [NSURL URLWithString:URLString]; - - return [self HTTPBodyWithDebugToken:debugToken] - .then(^FBLPromise *(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]; - }); -} - -#pragma mark - Helpers - -- (FBLPromise *)HTTPBodyWithDebugToken:(NSString *)debugToken { - if (debugToken.length <= 0) { - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise - reject:[GACAppCheckErrorUtil errorWithFailureReason:@"Debug token must not be empty."]]; - return rejectedPromise; - } - - return [FBLPromise onQueue:[self backgroundQueue] - do:^id _Nullable { - NSError *encodingError; - NSData *payloadJSON = [NSJSONSerialization - dataWithJSONObject:@{kDebugTokenField : debugToken} - options:0 - error:&encodingError]; - - if (payloadJSON != nil) { - return payloadJSON; - } else { - return [GACAppCheckErrorUtil JSONSerializationError:encodingError]; - } - }]; -} - -- (dispatch_queue_t)backgroundQueue { - return dispatch_get_global_queue(QOS_CLASS_UTILITY, 0); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/DebugProvider/GACAppCheckDebugProvider.m b/AppCheckCore/Sources/DebugProvider/GACAppCheckDebugProvider.m deleted file mode 100644 index 24ae7f58efb6..000000000000 --- a/AppCheckCore/Sources/DebugProvider/GACAppCheckDebugProvider.m +++ /dev/null @@ -1,124 +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 "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckDebugProvider.h" - -#if __has_include() -#import -#else -#import "FBLPromises.h" -#endif - -#import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h" -#import "AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h" -#import "AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -NS_ASSUME_NONNULL_BEGIN - -// TODO(andrewheard): Parameterize the following Firebase-specific keys. -// FIREBASE_APP_CHECK_ONLY_BEGIN -static NSString *const kDebugTokenEnvKey = @"FIRAAppCheckDebugToken"; -static NSString *const kDebugTokenUserDefaultsKey = @"FIRAAppCheckDebugToken"; -// FIREBASE_APP_CHECK_ONLY_END - -@interface GACAppCheckDebugProvider () -@property(nonatomic, readonly) id APIService; -@end - -@implementation GACAppCheckDebugProvider - -- (instancetype)initWithAPIService:(id)APIService { - self = [super init]; - if (self) { - _APIService = APIService; - } - return self; -} - -- (instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - APIKey:(nullable NSString *)APIKey - requestHooks:(nullable NSArray *)requestHooks { - NSURLSession *URLSession = [NSURLSession - sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; - - GACAppCheckAPIService *APIService = - [[GACAppCheckAPIService alloc] initWithURLSession:URLSession - baseURL:nil - APIKey:APIKey - requestHooks:requestHooks]; - - GACAppCheckDebugProviderAPIService *debugAPIService = - [[GACAppCheckDebugProviderAPIService alloc] initWithAPIService:APIService - resourceName:resourceName]; - - return [self initWithAPIService:debugAPIService]; -} - -- (NSString *)currentDebugToken { - NSString *envVariableValue = [[NSProcessInfo processInfo] environment][kDebugTokenEnvKey]; - if (envVariableValue.length > 0) { - return envVariableValue; - } else { - return [self localDebugToken]; - } -} - -- (NSString *)localDebugToken { - return [self storedDebugToken] ?: [self generateAndStoreDebugToken]; -} - -- (nullable NSString *)storedDebugToken { - return [[NSUserDefaults standardUserDefaults] stringForKey:kDebugTokenUserDefaultsKey]; -} - -- (void)storeDebugToken:(nullable NSString *)token { - [[NSUserDefaults standardUserDefaults] setObject:token forKey:kDebugTokenUserDefaultsKey]; -} - -- (NSString *)generateAndStoreDebugToken { - NSString *token = [NSUUID UUID].UUIDString; - [self storeDebugToken:token]; - return token; -} - -#pragma mark - GACAppCheckProvider - -- (void)getTokenWithCompletion:(void (^)(GACAppCheckToken *_Nullable token, - NSError *_Nullable error))handler { - [FBLPromise do:^NSString * { - return [self currentDebugToken]; - }] - .then(^FBLPromise *(NSString *debugToken) { - return [self.APIService appCheckTokenWithDebugToken:debugToken]; - }) - .then(^id(GACAppCheckToken *appCheckToken) { - handler(appCheckToken, nil); - return nil; - }) - .catch(^void(NSError *error) { - NSString *logMessage = [NSString - stringWithFormat:@"Failed to exchange debug token to app check token: %@", error]; - GACAppCheckLogDebug(GACLoggerAppCheckMessageDebugProviderFailedExchange, logMessage); - handler(nil, error); - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/DeviceCheckProvider/API/GACDeviceCheckAPIService.h b/AppCheckCore/Sources/DeviceCheckProvider/API/GACDeviceCheckAPIService.h deleted file mode 100644 index b3ea71533fe4..000000000000 --- a/AppCheckCore/Sources/DeviceCheckProvider/API/GACDeviceCheckAPIService.h +++ /dev/null @@ -1,44 +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 - -@class FBLPromise; -@class GACAppCheckToken; -@protocol GACAppCheckAPIServiceProtocol; - -NS_ASSUME_NONNULL_BEGIN - -@protocol GACDeviceCheckAPIServiceProtocol - -- (FBLPromise *)appCheckTokenWithDeviceToken:(NSData *)deviceToken; - -@end - -@interface GACDeviceCheckAPIService : NSObject - -/// Default initializer. -/// @param APIService An instance implementing `GACAppCheckAPIServiceProtocol` to be used to send -/// network requests to the App Check backend. -/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is -/// "projects/{project_id}/apps/{app_id}". See https://google.aip.dev/122 for more details about -/// resource names. -- (instancetype)initWithAPIService:(id)APIService - resourceName:(NSString *)resourceName; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.h b/AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.h deleted file mode 100644 index b0c2bcc90073..000000000000 --- a/AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2022 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. - */ - -<<<<<<<< HEAD:FirebaseAuth/Sources/Backend/RPC/FIRGetRecaptchaConfigResponse.m -#import "FirebaseAuth/Sources/Backend/RPC/FIRGetRecaptchaConfigResponse.h" -======== -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckAvailability.h" - -#if GAC_DEVICE_CHECK_SUPPORTED_TARGETS - -#import - -#import "AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckTokenGenerator.h" ->>>>>>>> e6cc60fb4 (Renamed `AppCheck` to `AppCheckCore` (#11581)):AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.h - -NS_ASSUME_NONNULL_BEGIN - -@implementation FIRGetRecaptchaConfigResponse - -- (BOOL)setWithDictionary:(NSDictionary *)dictionary error:(NSError *_Nullable *_Nullable)error { - _recaptchaKey = [dictionary[@"recaptchaKey"] copy]; - _enforcementState = dictionary[@"recaptchaEnforcementState"]; - return YES; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.m b/AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.m deleted file mode 100644 index e533587bd874..000000000000 --- a/AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.m +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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. - */ - -<<<<<<<< HEAD:AppCheckCore/Sources/Core/GACAppCheckSettings.m -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h" -======== -#import "AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.h" ->>>>>>>> e6cc60fb4 (Renamed `AppCheck` to `AppCheckCore` (#11581)):AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.m - -NS_ASSUME_NONNULL_BEGIN - -@implementation GACAppCheckSettings - -@synthesize isTokenAutoRefreshEnabled = _isTokenAutoRefreshEnabled; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckProvider.m b/AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckProvider.m deleted file mode 100644 index bbbb5c96aa20..000000000000 --- a/AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckProvider.m +++ /dev/null @@ -1,133 +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 "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckAvailability.h" - -#if GAC_DEVICE_CHECK_SUPPORTED_TARGETS - -#import - -#if __has_include() -#import -#else -#import "FBLPromises.h" -#endif - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACDeviceCheckProvider.h" - -#import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h" -#import "AppCheckCore/Sources/Core/Backoff/GACAppCheckBackoffWrapper.h" -#import "AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h" -#import "AppCheckCore/Sources/DeviceCheckProvider/API/GACDeviceCheckAPIService.h" -#import "AppCheckCore/Sources/DeviceCheckProvider/DCDevice+GACDeviceCheckTokenGenerator.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface GACDeviceCheckProvider () -@property(nonatomic, readonly) id APIService; -@property(nonatomic, readonly) id deviceTokenGenerator; -@property(nonatomic, readonly) id backoffWrapper; - -- (instancetype)initWithAPIService:(id)APIService - deviceTokenGenerator:(id)deviceTokenGenerator - backoffWrapper:(id)backoffWrapper - NS_DESIGNATED_INITIALIZER; - -@end - -@implementation GACDeviceCheckProvider - -- (instancetype)initWithAPIService:(id)APIService - deviceTokenGenerator:(id)deviceTokenGenerator - backoffWrapper:(id)backoffWrapper { - self = [super init]; - if (self) { - _APIService = APIService; - _deviceTokenGenerator = deviceTokenGenerator; - _backoffWrapper = backoffWrapper; - } - return self; -} - -- (instancetype)initWithAPIService:(id)APIService { - GACAppCheckBackoffWrapper *backoffWrapper = [[GACAppCheckBackoffWrapper alloc] init]; - return [self initWithAPIService:APIService - deviceTokenGenerator:[DCDevice currentDevice] - backoffWrapper:backoffWrapper]; -} - -- (instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - APIKey:(nullable NSString *)APIKey - requestHooks:(nullable NSArray *)requestHooks { - NSURLSession *URLSession = [NSURLSession - sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; - - GACAppCheckAPIService *APIService = - [[GACAppCheckAPIService alloc] initWithURLSession:URLSession - baseURL:nil - APIKey:APIKey - requestHooks:requestHooks]; - - GACDeviceCheckAPIService *deviceCheckAPIService = - [[GACDeviceCheckAPIService alloc] initWithAPIService:APIService resourceName:resourceName]; - - return [self initWithAPIService:deviceCheckAPIService]; -} - -#pragma mark - GACAppCheckProvider - -- (void)getTokenWithCompletion:(void (^)(GACAppCheckToken *_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(GACAppCheckToken *appCheckToken) { - handler(appCheckToken, nil); - return nil; - }) - .catch(^void(NSError *error) { - handler(nil, error); - }); -} - -- (FBLPromise *)getTokenPromise { - // Get DeviceCheck token - return [self deviceToken] - // Exchange DeviceCheck token for FAC token. - .then(^FBLPromise *(NSData *deviceToken) { - return [self.APIService appCheckTokenWithDeviceToken:deviceToken]; - }); -} - -#pragma mark - DeviceCheck - -- (FBLPromise *)deviceToken { - return [FBLPromise - wrapObjectOrErrorCompletion:^(FBLPromiseObjectOrErrorCompletion _Nonnull handler) { - [self.deviceTokenGenerator generateTokenWithCompletionHandler:handler]; - }]; -} - -@end - -NS_ASSUME_NONNULL_END - -#endif // GAC_DEVICE_CHECK_SUPPORTED_TARGETS diff --git a/AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckTokenGenerator.h b/AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckTokenGenerator.h deleted file mode 100644 index 1f88b7d8d881..000000000000 --- a/AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckTokenGenerator.h +++ /dev/null @@ -1,28 +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 - -NS_ASSUME_NONNULL_BEGIN - -@protocol GACDeviceCheckTokenGenerator - -- (void)generateTokenWithCompletionHandler:(void (^)(NSData* _Nullable token, - NSError* _Nullable error))completion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Public/AppCheckCore/AppCheck.h b/AppCheckCore/Sources/Public/AppCheckCore/AppCheck.h deleted file mode 100644 index e55dfbbab07f..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/AppCheck.h +++ /dev/null @@ -1,32 +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 "GACAppCheck.h" -#import "GACAppCheckErrors.h" -#import "GACAppCheckLogger.h" -#import "GACAppCheckProvider.h" -#import "GACAppCheckSettings.h" -#import "GACAppCheckToken.h" -#import "GACAppCheckTokenDelegate.h" - -// Debug provider -#import "GACAppCheckDebugProvider.h" - -// DeviceCheck provider -#import "GACDeviceCheckProvider.h" - -// App Attest provider. -#import "GACAppAttestProvider.h" diff --git a/AppCheckCore/Sources/Public/AppCheckCore/AppCheckCore.h b/AppCheckCore/Sources/Public/AppCheckCore/AppCheckCore.h deleted file mode 100644 index e55dfbbab07f..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/AppCheckCore.h +++ /dev/null @@ -1,32 +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 "GACAppCheck.h" -#import "GACAppCheckErrors.h" -#import "GACAppCheckLogger.h" -#import "GACAppCheckProvider.h" -#import "GACAppCheckSettings.h" -#import "GACAppCheckToken.h" -#import "GACAppCheckTokenDelegate.h" - -// Debug provider -#import "GACAppCheckDebugProvider.h" - -// DeviceCheck provider -#import "GACDeviceCheckProvider.h" - -// App Attest provider. -#import "GACAppAttestProvider.h" diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppAttestProvider.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppAttestProvider.h deleted file mode 100644 index 3ba88372fda4..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppAttestProvider.h +++ /dev/null @@ -1,81 +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 - -#import "GACAppCheckProvider.h" - -#import "GACAppCheckAvailability.h" - -NS_ASSUME_NONNULL_BEGIN - -/// Firebase App Check provider that verifies app integrity using the -/// [DeviceCheck](https://developer.apple.com/documentation/devicecheck/dcappattestservice) API. -/// This class is available on all platforms for select OS versions. See -/// https://firebase.google.com/docs/ios/learn-more for more details. -GAC_APP_ATTEST_PROVIDER_AVAILABILITY -NS_SWIFT_NAME(AppCheckCoreAppAttestProvider) -@interface GACAppAttestProvider : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -/// The default initializer. -/// @param serviceName A unique identifier to differentiate storage keys corresponding to the same -/// `resourceName`; may be a Firebase App Name or an SDK name. -/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is -/// "projects/{project_id}/apps/{app_id}". -/// @param baseURL The base URL for the App Check service; defaults to -/// `https://firebaseappcheck.googleapis.com/v1` if nil. -/// @param APIKey The Google Cloud Platform API key, if needed, or nil. -/// @param accessGroup The Keychain Access Group. -/// @param requestHooks Hooks that will be invoked on requests through this service. -/// @return An instance of `AppAttestProvider` if App Attest is supported or `nil`. -- (nullable instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - baseURL:(nullable NSString *)baseURL - APIKey:(nullable NSString *)APIKey - keychainAccessGroup:(nullable NSString *)accessGroup - requestHooks: - (nullable NSArray *)requestHooks; - -/// Initializer with support for short-lived tokens. -/// -/// TODO(andrewheard): Remove or refactor this constructor when the short-lived (limited-use) token -/// feature is fully implemented. -/// -/// @param serviceName A unique identifier to differentiate storage keys corresponding to the same -/// `resourceName`; may be a Firebase App Name or an SDK name. -/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is -/// "projects/{project_id}/apps/{app_id}". -/// @param baseURL The base URL for the App Check service; defaults to -/// `https://firebaseappcheck.googleapis.com/v1` if nil. -/// @param APIKey The Google Cloud Platform API key, if needed, or nil. -/// @param accessGroup The Keychain Access Group. -/// @param limitedUse If YES, forces a short-lived token with a 5 minute TTL. -/// @param requestHooks Hooks that will be invoked on requests through this service. -/// @return An instance of `AppAttestProvider` if App Attest is supported or `nil`. -- (nullable instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - baseURL:(nullable NSString *)baseURL - APIKey:(nullable NSString *)APIKey - keychainAccessGroup:(nullable NSString *)accessGroup - limitedUse:(BOOL)limitedUse - requestHooks: - (nullable NSArray *)requestHooks; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheck.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheck.h deleted file mode 100644 index a3f68c2c26bb..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheck.h +++ /dev/null @@ -1,77 +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 - -@class GACAppCheckToken; -@protocol GACAppCheckProvider; -@protocol GACAppCheckSettingsProtocol; -@protocol GACAppCheckTokenDelegate; -@protocol GACAppCheckTokenProtocol; - -NS_ASSUME_NONNULL_BEGIN - -NS_SWIFT_NAME(AppCheckCoreProtocol) @protocol GACAppCheckProtocol - -/// Requests Firebase app check token. -/// -/// @param forcingRefresh If `YES`, a new Firebase app check token is requested and the token -/// cache is ignored. If `NO`, the cached token is used if it exists and has not expired yet. In -/// most cases, `NO` should be used. `YES` should only be used if the server explicitly returns an -/// error, indicating a revoked token. -/// @param handler The completion handler. Includes the app check token if the request succeeds, -/// or an error if the request fails. -- (void)getTokenForcingRefresh:(BOOL)forcingRefresh - completion:(void (^)(id _Nullable token, - NSError *_Nullable error))handler - NS_SWIFT_NAME(token(forcingRefresh:completion:)); - -/// Retrieve a new limited-use App Check token -/// -/// This method does not affect the token generation behavior of the -/// ``tokenForcingRefresh()`` method. -- (void)getLimitedUseTokenWithCompletion:(void (^)(id _Nullable token, - NSError *_Nullable error))handler; - -@end - -/// A class used to manage App Check tokens for a given resource. -NS_SWIFT_NAME(AppCheckCore) -@interface GACAppCheck : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -/// Returns an instance of `AppCheck` for an application. -/// @param serviceName A unique identifier for the App Check instance, may be a Firebase App Name -/// or an SDK name. -/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is -/// "projects/{project_id}/apps/{app_id}". -/// @param appCheckProvider An object that provides App Check tokens. -/// @param settings An object that provides App Check settings. -/// @param tokenDelegate A delegate that receives token update notifications. -/// @param accessGroup The identifier for a keychain group that the app shares items with; if -/// provided, requires the Keychain Access Groups Entitlement. -/// @return An instance of `AppCheckCore` with the specified token provider. -- (instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - appCheckProvider:(id)appCheckProvider - settings:(id)settings - tokenDelegate:(nullable id)tokenDelegate - keychainAccessGroup:(nullable NSString *)accessGroup; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckAvailability.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckAvailability.h deleted file mode 100644 index 07fe04415127..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckAvailability.h +++ /dev/null @@ -1,69 +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. - */ - -// Availability conditions for different App Check SDK components. - -#import -#import - -#pragma mark - DeviceCheck - -// DeviceCheck availability was extended to watchOS in Xcode 14. -#if defined(__WATCHOS_9_0) && __WATCH_OS_VERSION_MAX_ALLOWED >= __WATCHOS_9_0 - -// Targets where DeviceCheck framework is available to be used in preprocessor conditions. -#define GAC_DEVICE_CHECK_SUPPORTED_TARGETS \ - TARGET_OS_IOS || TARGET_OS_OSX || TARGET_OS_TV || TARGET_OS_WATCH - -// `DeviceCheckProvider` availability. -#define GAC_DEVICE_CHECK_PROVIDER_AVAILABILITY \ - API_AVAILABLE(ios(11.0), macos(10.15), tvos(11.0), watchos(9.0)) - -// TODO(ncooke3): Remove `#else` clause when Xcode 14 is the minimum supported Xcode. -#else // defined(__WATCHOS_9_0) && __WATCH_OS_VERSION_MAX_ALLOWED >= __WATCHOS_9_0 - -// Targets where DeviceCheck framework is available to be used in preprocessor conditions. -#define GAC_DEVICE_CHECK_SUPPORTED_TARGETS TARGET_OS_IOS || TARGET_OS_OSX || TARGET_OS_TV - -// `DeviceCheckProvider` availability. -#define GAC_DEVICE_CHECK_PROVIDER_AVAILABILITY \ - API_AVAILABLE(ios(11.0), macos(10.15), tvos(11.0)) API_UNAVAILABLE(watchos) - -#endif // defined(__WATCHOS_9_0) && __WATCH_OS_VERSION_MAX_ALLOWED >= __WATCHOS_9_0 - -#pragma mark - App Attest - -#if defined(__WATCHOS_9_0) && __WATCH_OS_VERSION_MAX_ALLOWED >= __WATCHOS_9_0 - -// Targets where `DCAppAttestService` is available to be used in preprocessor conditions. -#define GAC_APP_ATTEST_SUPPORTED_TARGETS \ - TARGET_OS_IOS || TARGET_OS_OSX || TARGET_OS_TV || TARGET_OS_WATCH - -// `AppAttestProvider` availability annotations -#define GAC_APP_ATTEST_PROVIDER_AVAILABILITY \ - API_AVAILABLE(macos(11.0), ios(14.0), tvos(15.0), watchos(9.0)) - -// TODO(ncooke3): Remove `#else` clause when Xcode 14 is the minimum supported Xcode. -#else // defined(__WATCHOS_9_0) && __WATCH_OS_VERSION_MAX_ALLOWED >= __WATCHOS_9_0 - -// Targets where `DCAppAttestService` is available to be used in preprocessor conditions. -#define GAC_APP_ATTEST_SUPPORTED_TARGETS TARGET_OS_IOS || TARGET_OS_OSX || TARGET_OS_TV - -// `AppAttestProvider` availability annotations -#define GAC_APP_ATTEST_PROVIDER_AVAILABILITY \ - API_AVAILABLE(macos(11.0), ios(14.0), tvos(15.0)) API_UNAVAILABLE(watchos) - -#endif // defined(__WATCHOS_9_0) && __WATCH_OS_VERSION_MAX_ALLOWED >= __WATCHOS_9_0 diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckDebugProvider.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckDebugProvider.h deleted file mode 100644 index 43773467cbb1..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckDebugProvider.h +++ /dev/null @@ -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 - -#import "GACAppCheckProvider.h" - -NS_ASSUME_NONNULL_BEGIN - -/// A Firebase App Check provider that can exchange a debug token registered -/// in the Firebase console for a Firebase App Check token. The debug provider -/// is designed to enable testing applications on a simulator or test -/// environment. -/// -/// NOTE: Do not use the debug provider in applications used by real users. -/// -/// WARNING: Keep the Firebase App Check debug token secret. If you -/// accidentally share one (e.g. commit to a public source repo), remove it in -/// the Firebase console ASAP. -/// -/// To use `AppCheckDebugProvider` on a local simulator: -/// 1. Configure `AppCheckDebugProviderFactory` before `FirebaseApp.configure()`: -/// ```AppCheck.setAppCheckProviderFactory(AppCheckDebugProviderFactory())``` -/// 2. Enable debug logging by adding the `-FIRDebugEnabled` launch argument to -/// the app target. -/// 3. Launch the app. A local debug token will be logged when Firebase is -/// configured. For example: -/// "[Firebase/AppCheck][I-FAA001001] Firebase App Check Debug Token: -/// '3BA09C8C-8A0D-4030-ACD5-B96D99DB73F9'". -/// 4. Register the debug token in the Firebase console. -/// -/// Once the debug token is registered the debug provider will be able to provide a valid Firebase -/// App Check token. -/// -/// To use `AppCheckDebugProvider` on a simulator on a build server: -/// 1. Create a new Firebase App Check debug token in the Firebase console -/// 2. Add the debug token to the secure storage of your build environment. E.g. see [Encrypted -/// secrets](https://docs.github.com/en/actions/reference/encrypted-secrets) for GitHub Actions, -/// etc. -/// 3. Configure `AppCheckDebugProviderFactory` before `FirebaseApp.configure()` -/// `AppCheck.setAppCheckProviderFactory(AppCheckDebugProviderFactory())` -/// 4. Add an environment variable to the scheme with a name `FIRAAppCheckDebugToken` and value like -/// `$(MY_APP_CHECK_DEBUG_TOKEN)`. -/// 5. Configure the build script to pass the debug token as the environment variable, e.g.: -/// `xcodebuild test -scheme InstallationsExample -workspace InstallationsExample.xcworkspace \ -/// MY_APP_CHECK_DEBUG_TOKEN=$(MY_SECRET_ON_CI)` -/// -NS_SWIFT_NAME(AppCheckCoreDebugProvider) -@interface GACAppCheckDebugProvider : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -/// The default initializer. -/// @param serviceName A unique identifier to differentiate storage keys corresponding to the same -/// `resourceName`; may be a Firebase App Name or an SDK name. -/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is -/// "projects/{project_id}/apps/{app_id}". -/// @param APIKey The Google Cloud Platform API key, if needed, or nil. -/// @param requestHooks Hooks that will be invoked on requests through this service. -/// @return An instance of `AppCheckDebugProvider` . -- (instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - APIKey:(nullable NSString *)APIKey - requestHooks:(nullable NSArray *)requestHooks; - -/** Return the locally generated token. */ -- (NSString *)localDebugToken; - -/** Returns the currently used App Check debug token. The priority: - * - `FIRAAppCheckDebugToken` env variable value - * - A previously generated token, stored locally on the device - * - A newly generated random token. The generated token will be stored - * locally for future use - * @return The currently used App Check debug token. - */ -- (NSString *)currentDebugToken; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h deleted file mode 100644 index 48a551b3d00c..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h +++ /dev/null @@ -1,56 +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 - -/// Firebase app check error domain. -FOUNDATION_EXTERN NSErrorDomain const GACAppCheckErrorDomain NS_SWIFT_NAME(AppCheckCoreErrorDomain); - -typedef NS_ERROR_ENUM(GACAppCheckErrorDomain, GACAppCheckErrorCode){ - /// An unknown or non-actionable error. - GACAppCheckErrorCodeUnknown = 0, - - /// A network connection error. - GACAppCheckErrorCodeServerUnreachable = 1, - - /// Invalid configuration error. Currently, an exception is thrown but this error is reserved - /// for future implementations of invalid configuration detection. - GACAppCheckErrorCodeInvalidConfiguration = 2, - - /// System keychain access error. Ensure that the app has proper keychain access. - GACAppCheckErrorCodeKeychain = 3, - - /// Selected app attestation provider is not supported on the current platform or OS version. - GACAppCheckErrorCodeUnsupported = 4 - -} NS_SWIFT_NAME(AppCheckCoreErrorCode); - -#pragma mark - Error Message Codes - -typedef NS_ENUM(NSInteger, GACAppCheckMessageCode) { - GACLoggerAppCheckMessageCodeUnknown = 1001, - - // App Check - GACLoggerAppCheckMessageCodeProviderIsMissing = 2002, - GACLoggerAppCheckMessageCodeUnexpectedHTTPCode = 3001, - - // Debug Provider - GACLoggerAppCheckMessageDebugProviderFailedExchange = 4002, - - // App Attest Provider - GACLoggerAppCheckMessageCodeAppAttestNotSupported = 7001, - GACLoggerAppCheckMessageCodeAttestationRejected = 7002 -} NS_SWIFT_NAME(AppCheckCoreMessageCode); diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckLogger.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckLogger.h deleted file mode 100644 index cab60ceeb480..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckLogger.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 "GACAppCheckErrors.h" - -/// Constants that specify the level of logging to perform in App Check Core. -typedef NS_ENUM(NSInteger, GACAppCheckLogLevel) { - /// The debug log level; equivalent to `OS_LOG_TYPE_DEBUG`. - GACAppCheckLogLevelDebug = 1, - /// The informational log level; equivalent to `OS_LOG_TYPE_INFO`. - GACAppCheckLogLevelInfo = 2, - /// The warning log level; equivalent to `OS_LOG_TYPE_DEFAULT`. - GACAppCheckLogLevelWarning = 3, - /// The error log level; equivalent to `OS_LOG_TYPE_ERROR`. - GACAppCheckLogLevelError = 4, - /// The fault log level; equivalent to `OS_LOG_TYPE_FAULT`. - GACAppCheckLogLevelFault = 5 -} NS_SWIFT_NAME(AppCheckCoreLogLevel); - -NS_SWIFT_NAME(AppCheckCoreLogger) -@interface GACAppCheckLogger : NSObject - -/// The current logging level. -/// -/// Messages with levels equal to or higher priority than `logLevel` will be printed, where -/// Fault > Error > Warning > Info > Debug. -@property(class, atomic, assign) GACAppCheckLogLevel logLevel; - -- (instancetype)init NS_UNAVAILABLE; - -@end diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckProvider.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckProvider.h deleted file mode 100644 index f453dc6a1eb5..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckProvider.h +++ /dev/null @@ -1,41 +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 - -@class GACAppCheckToken; - -NS_ASSUME_NONNULL_BEGIN - -/// A block to be called before sending API requests. -/// @param request The request that is about to be sent. -typedef void (^GACAppCheckAPIRequestHook)(NSMutableURLRequest *request); - -/// Defines the methods required to be implemented by a specific Firebase App Check -/// provider. -NS_SWIFT_NAME(AppCheckCoreProvider) -@protocol GACAppCheckProvider - -/// Returns a new Firebase App Check token. -/// @param handler The completion handler. Make sure to call the handler with either a token -/// or an error. -- (void)getTokenWithCompletion: - (void (^)(GACAppCheckToken *_Nullable token, NSError *_Nullable error))handler - NS_SWIFT_NAME(getToken(completion:)); - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h deleted file mode 100644 index d0092d82becf..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 - -NS_ASSUME_NONNULL_BEGIN - -/// A collection of App Check-wide settings and parameters. -NS_SWIFT_NAME(AppCheckCoreSettingsProtocol) -@protocol GACAppCheckSettingsProtocol - -/// If App Check token auto-refresh is enabled. -@property(nonatomic, assign) BOOL isTokenAutoRefreshEnabled; - -@end - -@interface GACAppCheckSettings : NSObject -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h deleted file mode 100644 index 8e0c47e6c142..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h +++ /dev/null @@ -1,57 +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 - -NS_ASSUME_NONNULL_BEGIN - -NS_SWIFT_NAME(AppCheckCoreTokenProtocol) -@protocol GACAppCheckTokenProtocol - -/// A Firebase App Check token. -@property(nonatomic, readonly) NSString *token; - -/// The App Check token's expiration date in the device's local time. -@property(nonatomic, readonly) NSDate *expirationDate; - -/// The date when the App Check token was received in the device's local time. -@property(nonatomic, readonly) NSDate *receivedAtDate; - -@end - -/// An object representing a Firebase App Check token. -NS_SWIFT_NAME(AppCheckCoreToken) -@interface GACAppCheckToken : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -/// Convenience initializer that uses the current device local time to set `receivedAtDate`. -/// @param token A Firebase App Check token. -/// @param expirationDate A Firebase App Check token expiration date in the device local time. -- (instancetype)initWithToken:(NSString *)token expirationDate:(NSDate *)expirationDate; - -/// The designated initializer. -/// @param token A Firebase App Check token. -/// @param expirationDate A Firebase App Check token expiration date in the device local time. -/// @param receivedAtDate A date when the Firebase App Check token was received in the device's -/// local time. -- (instancetype)initWithToken:(NSString *)token - expirationDate:(NSDate *)expirationDate - receivedAtDate:(NSDate *)receivedAtDate NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckTokenDelegate.h b/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckTokenDelegate.h deleted file mode 100644 index be5d9b1713e7..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckTokenDelegate.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 - -NS_ASSUME_NONNULL_BEGIN - -NS_SWIFT_NAME(AppCheckCoreTokenDelegate) -@protocol GACAppCheckTokenDelegate - -/// Called each time an App Check token is refreshed. -/// -/// @param token The updated App Check token. -/// @param serviceName A unique identifier for the App Check instance, may be a Firebase App Name -/// or an SDK name. -- (void)tokenDidUpdate:(GACAppCheckToken *)token serviceName:(NSString *)serviceName; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Sources/Public/AppCheckCore/GACDeviceCheckProvider.h b/AppCheckCore/Sources/Public/AppCheckCore/GACDeviceCheckProvider.h deleted file mode 100644 index da6f1d1f46b4..000000000000 --- a/AppCheckCore/Sources/Public/AppCheckCore/GACDeviceCheckProvider.h +++ /dev/null @@ -1,56 +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 - -#import "GACAppCheckAvailability.h" -#import "GACAppCheckProvider.h" - -#if GAC_DEVICE_CHECK_SUPPORTED_TARGETS - -@protocol GACDeviceCheckAPIServiceProtocol; -@protocol GACDeviceCheckTokenGenerator; - -NS_ASSUME_NONNULL_BEGIN - -/// Firebase App Check provider that verifies app integrity using the -/// [DeviceCheck](https://developer.apple.com/documentation/devicecheck) API. -/// This class is available on all platforms for select OS versions. See -/// https://firebase.google.com/docs/ios/learn-more for more details. -GAC_DEVICE_CHECK_PROVIDER_AVAILABILITY -NS_SWIFT_NAME(AppCheckCoreDeviceCheckProvider) -@interface GACDeviceCheckProvider : NSObject - -- (instancetype)init NS_UNAVAILABLE; - -/// The default initializer. -/// @param serviceName A unique identifier to differentiate storage keys corresponding to the same -/// `resourceName`; may be a Firebase App Name or an SDK name. -/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is -/// "projects/{project_id}/apps/{app_id}". -/// @param APIKey The Google Cloud Platform API key, if needed, or nil. -/// @param requestHooks Hooks that will be invoked on requests through this service. -/// @return An instance of `DeviceCheckProvider` . -- (instancetype)initWithServiceName:(NSString *)serviceName - resourceName:(NSString *)resourceName - APIKey:(nullable NSString *)APIKey - requestHooks:(nullable NSArray *)requestHooks; - -@end - -NS_ASSUME_NONNULL_END - -#endif // GAC_DEVICE_CHECK_SUPPORTED_TARGETS diff --git a/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m b/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m deleted file mode 100644 index 72c6bee69779..000000000000 --- a/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m +++ /dev/null @@ -1,590 +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 - -#import -#import "FBLPromise+Testing.h" - -#import - -#import "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.h" -#import "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h" -#import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -#import "AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.h" -#import "AppCheckCore/Tests/Utils/Date/GACDateTestUtils.h" -#import "AppCheckCore/Tests/Utils/URLSession/GACURLSessionOCMockStub.h" - -static NSString *const kBaseURL = @"https://test.appcheck.url.com/beta"; -static NSString *const kResourceName = @"projects/project_id/apps/app_id"; - -@interface GACAppAttestAPIServiceTests : XCTestCase - -@property(nonatomic) GACAppAttestAPIService *appAttestAPIService; - -@property(nonatomic) id mockAPIService; - -@end - -@implementation GACAppAttestAPIServiceTests - -- (void)setUp { - [super setUp]; - - self.mockAPIService = OCMProtocolMock(@protocol(GACAppCheckAPIServiceProtocol)); - OCMStub([self.mockAPIService baseURL]).andReturn(kBaseURL); - - self.appAttestAPIService = [[GACAppAttestAPIService alloc] initWithAPIService:self.mockAPIService - resourceName:kResourceName - limitedUse:NO]; -} - -- (void)tearDown { - [super tearDown]; - - self.appAttestAPIService = nil; - [self.mockAPIService stopMocking]; - self.mockAPIService = nil; -} - -#pragma mark - Random challenge request - -- (void)testGetRandomChallengeWhenAPIResponseValid { - // 1. Prepare API response. - NSData *responseBody = [GACFixtureLoader loadFixtureNamed:@"AppAttestResponseSuccess.json"]; - GULURLSessionDataResponse *validAPIResponse = [self APIResponseWithCode:200 - responseBody:responseBody]; - // 2. Stub API Service Request to return prepared API response. - [self stubMockAPIServiceRequestForChallengeRequestWithResponse:validAPIResponse]; - - // 3. Request the random challenge and verify results. - __auto_type *promise = [self.appAttestAPIService getRandomChallenge]; - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - XCTAssert(promise.isFulfilled); - XCTAssertNotNil(promise.value); - XCTAssertNil(promise.error); - - NSString *challengeString = [[NSString alloc] initWithData:promise.value - encoding:NSUTF8StringEncoding]; - // The challenge stored in `AppAttestResponseSuccess.json` is a valid base64 encoding of - // the string "random_challenge". - XCTAssert([challengeString isEqualToString:@"random_challenge"]); - - OCMVerifyAll(self.mockAPIService); -} - -- (void)testGetRandomChallengeWhenAPIError { - // 1. Prepare API response. - NSString *responseBodyString = @"Generate challenge failed with invalid format."; - NSData *responseBody = [responseBodyString dataUsingEncoding:NSUTF8StringEncoding]; - GULURLSessionDataResponse *invalidAPIResponse = [self APIResponseWithCode:300 - responseBody:responseBody]; - GACAppCheckHTTPError *APIError = - [GACAppCheckErrorUtil APIErrorWithHTTPResponse:invalidAPIResponse.HTTPResponse - data:invalidAPIResponse.HTTPBody]; - // 2. Stub API Service Request to return prepared API response. - [self stubMockAPIServiceRequestForChallengeRequestWithResponse:APIError]; - - // 3. Request the random challenge and verify results. - __auto_type *promise = [self.appAttestAPIService getRandomChallenge]; - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - XCTAssert(promise.isRejected); - XCTAssertNotNil(promise.error); - XCTAssertNil(promise.value); - - // Assert error is as expected. - XCTAssertEqualObjects(promise.error.domain, GACAppCheckErrorDomain); - XCTAssertEqual(promise.error.code, GACAppCheckErrorCodeUnknown); - - // Expect response body and HTTP status code to be included in the error. - NSString *failureReason = promise.error.userInfo[NSLocalizedFailureReasonErrorKey]; - XCTAssertTrue([failureReason containsString:@"300"]); - XCTAssertTrue([failureReason containsString:responseBodyString]); - - OCMVerifyAll(self.mockAPIService); -} - -- (void)testGetRandomChallengeWhenAPIResponseEmpty { - // 1. Prepare API response. - NSData *responseBody = [NSData data]; - GULURLSessionDataResponse *emptyAPIResponse = [self APIResponseWithCode:200 - responseBody:responseBody]; - // 2. Stub API Service Request to return prepared API response. - [self stubMockAPIServiceRequestForChallengeRequestWithResponse:emptyAPIResponse]; - - // 3. Request the random challenge and verify results. - __auto_type *promise = [self.appAttestAPIService getRandomChallenge]; - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - XCTAssert(promise.isRejected); - XCTAssertNotNil(promise.error); - XCTAssertNil(promise.value); - - // Expect response body and HTTP status code to be included in the error. - NSString *failureReason = promise.error.userInfo[NSLocalizedFailureReasonErrorKey]; - XCTAssertEqualObjects(failureReason, @"Empty server response body."); - - OCMVerifyAll(self.mockAPIService); -} - -- (void)testGetRandomChallengeWhenAPIResponseInvalidFormat { - // 1. Prepare API response. - NSString *responseBodyString = @"Generate challenge failed with invalid format."; - NSData *responseBody = [responseBodyString dataUsingEncoding:NSUTF8StringEncoding]; - GULURLSessionDataResponse *validAPIResponse = [self APIResponseWithCode:200 - responseBody:responseBody]; - // 2. Stub API Service Request to return prepared API response. - [self stubMockAPIServiceRequestForChallengeRequestWithResponse:validAPIResponse]; - - // 3. Request the random challenge and verify results. - __auto_type *promise = [self.appAttestAPIService getRandomChallenge]; - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - XCTAssert(promise.isRejected); - XCTAssertNotNil(promise.error); - XCTAssertNil(promise.value); - - // Expect response body and HTTP status code to be included in the error. - NSString *failureReason = promise.error.userInfo[NSLocalizedFailureReasonErrorKey]; - XCTAssertEqualObjects(failureReason, @"JSON serialization error."); - - OCMVerifyAll(self.mockAPIService); -} - -- (void)testGetRandomChallengeWhenResponseMissingField { - [self assertMissingFieldErrorWithFixture:@"AppAttestResponseMissingChallenge.json" - missingField:@"challenge"]; -} - -- (void)assertMissingFieldErrorWithFixture:(NSString *)fixtureName - missingField:(NSString *)fieldName { - // 1. Prepare API response. - NSData *missingFieldBody = [GACFixtureLoader loadFixtureNamed:fixtureName]; - GULURLSessionDataResponse *incompleteAPIResponse = [self APIResponseWithCode:200 - responseBody:missingFieldBody]; - // 2. Stub API Service Request to return prepared API response. - [self stubMockAPIServiceRequestForChallengeRequestWithResponse:incompleteAPIResponse]; - - // 3. Request the random challenge and verify results. - __auto_type *promise = [self.appAttestAPIService getRandomChallenge]; - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - XCTAssert(promise.isRejected); - XCTAssertNotNil(promise.error); - XCTAssertNil(promise.value); - - // Assert error is as expected. - XCTAssertEqualObjects(promise.error.domain, GACAppCheckErrorDomain); - XCTAssertEqual(promise.error.code, GACAppCheckErrorCodeUnknown); - - // Expect missing field name to be included in the error. - NSString *failureReason = promise.error.userInfo[NSLocalizedFailureReasonErrorKey]; - NSString *fieldNameString = [NSString stringWithFormat:@"`%@`", fieldName]; - XCTAssertTrue([failureReason containsString:fieldNameString], - @"Fixture `%@`: expected missing field %@ error not found", fixtureName, - fieldNameString); -} - -#pragma mark - Assertion request - -- (void)testGetAppCheckTokenSuccess { - NSData *artifact = [self generateRandomData]; - NSData *challenge = [self generateRandomData]; - NSData *assertion = [self generateRandomData]; - - // 1. Prepare response. - NSData *responseBody = - [GACFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"]; - GULURLSessionDataResponse *validAPIResponse = [self APIResponseWithCode:200 - responseBody:responseBody]; - - // 2. Stub API Service - // 2.1. Return prepared response. - [self expectTokenAPIRequestWithArtifact:artifact - challenge:challenge - assertion:assertion - response:validAPIResponse - error:nil]; - // 2.2. Return token from parsed response. - GACAppCheckToken *expectedToken = [[GACAppCheckToken alloc] initWithToken:@"app_check_token" - expirationDate:[NSDate date] - receivedAtDate:[NSDate date]]; - [self expectTokenWithAPIReponse:validAPIResponse toReturnToken:expectedToken]; - - // 3. Send request. - __auto_type promise = [self.appAttestAPIService getAppCheckTokenWithArtifact:artifact - challenge:challenge - assertion:assertion]; - // 4. Verify. - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - - XCTAssertTrue(promise.isFulfilled); - XCTAssertNil(promise.error); - - XCTAssertEqualObjects(promise.value, expectedToken); - XCTAssertEqualObjects(promise.value.token, expectedToken.token); - XCTAssertEqualObjects(promise.value.expirationDate, expectedToken.expirationDate); - XCTAssertEqualObjects(promise.value.receivedAtDate, expectedToken.receivedAtDate); - - OCMVerifyAll(self.mockAPIService); -} - -- (void)testGetAppCheckTokenNetworkError { - NSData *artifact = [self generateRandomData]; - NSData *challenge = [self generateRandomData]; - NSData *assertion = [self generateRandomData]; - - // 1. Prepare response. - NSData *responseBody = - [GACFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"]; - GULURLSessionDataResponse *validAPIResponse = [self APIResponseWithCode:200 - responseBody:responseBody]; - - // 2. Stub API Service - // 2.1. Return prepared response. - NSError *networkError = [NSError errorWithDomain:self.name code:0 userInfo:nil]; - [self expectTokenAPIRequestWithArtifact:artifact - challenge:challenge - assertion:assertion - response:validAPIResponse - error:networkError]; - - // 3. Send request. - __auto_type promise = [self.appAttestAPIService getAppCheckTokenWithArtifact:artifact - challenge:challenge - assertion:assertion]; - // 4. Verify. - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - - XCTAssertTrue(promise.isRejected); - XCTAssertNil(promise.value); - XCTAssertEqualObjects(promise.error, networkError); - - OCMVerifyAll(self.mockAPIService); -} - -- (void)testGetAppCheckTokenUnexpectedResponse { - NSData *artifact = [self generateRandomData]; - NSData *challenge = [self generateRandomData]; - NSData *assertion = [self generateRandomData]; - - // 1. Prepare response. - NSData *responseBody = - [GACFixtureLoader loadFixtureNamed:@"DeviceCheckResponseMissingToken.json"]; - GULURLSessionDataResponse *validAPIResponse = [self APIResponseWithCode:200 - responseBody:responseBody]; - - // 2. Stub API Service - // 2.1. Return prepared response. - [self expectTokenAPIRequestWithArtifact:artifact - challenge:challenge - assertion:assertion - response:validAPIResponse - error:nil]; - // 2.2. Return token from parsed response. - [self expectTokenWithAPIReponse:validAPIResponse toReturnToken:nil]; - - // 3. Send request. - __auto_type promise = [self.appAttestAPIService getAppCheckTokenWithArtifact:artifact - challenge:challenge - assertion:assertion]; - // 4. Verify. - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - - XCTAssertTrue(promise.isRejected); - XCTAssertNil(promise.value); - XCTAssertNotNil(promise.error); - - OCMVerifyAll(self.mockAPIService); -} - -#pragma mark - Attestation request - -- (void)testAttestKeySuccess { - NSData *attestation = [self generateRandomData]; - NSData *challenge = [self generateRandomData]; - NSString *keyID = [NSUUID UUID].UUIDString; - - // 1. Prepare response. - NSData *responseBody = - [GACFixtureLoader loadFixtureNamed:@"AppAttestAttestationResponseSuccess.json"]; - GULURLSessionDataResponse *validAPIResponse = [self APIResponseWithCode:200 - responseBody:responseBody]; - - // 2. Stub API Service - // 2.1. Return prepared response. - [self expectAttestAPIRequestWithAttestation:attestation - keyID:keyID - challenge:challenge - response:validAPIResponse - error:nil]; - - // 3. Send request. - __auto_type promise = [self.appAttestAPIService attestKeyWithAttestation:attestation - keyID:keyID - challenge:challenge]; - - // 4. Verify. - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - - XCTAssertTrue(promise.isFulfilled); - XCTAssertNil(promise.error); - - NSData *expectedArtifact = - [@"valid Firebase app attest artifact" dataUsingEncoding:NSUTF8StringEncoding]; - - XCTAssertEqualObjects(promise.value.artifact, expectedArtifact); - XCTAssertEqualObjects(promise.value.token.token, @"valid_app_check_token"); - XCTAssertTrue([GACDateTestUtils isDate:promise.value.token.expirationDate - approximatelyEqualCurrentPlusTimeInterval:1800 - precision:10]); - - OCMVerifyAll(self.mockAPIService); -} - -- (void)testAttestKeyNetworkError { - NSData *attestation = [self generateRandomData]; - NSData *challenge = [self generateRandomData]; - NSString *keyID = [NSUUID UUID].UUIDString; - - // 1. Stub API Service - // 1.1. Return prepared response. - NSError *networkError = [NSError errorWithDomain:self.name code:0 userInfo:nil]; - [self expectAttestAPIRequestWithAttestation:attestation - keyID:keyID - challenge:challenge - response:nil - error:networkError]; - - // 2. Send request. - __auto_type promise = [self.appAttestAPIService attestKeyWithAttestation:attestation - keyID:keyID - challenge:challenge]; - - // 3. Verify. - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - - XCTAssertTrue(promise.isRejected); - XCTAssertNil(promise.value); - XCTAssertEqualObjects(promise.error, networkError); - - OCMVerifyAll(self.mockAPIService); -} - -- (void)testAttestKeyUnexpectedResponse { - NSData *attestation = [self generateRandomData]; - NSData *challenge = [self generateRandomData]; - NSString *keyID = [NSUUID UUID].UUIDString; - - // 1. Prepare unexpected response. - NSData *responseBody = - [GACFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"]; - GULURLSessionDataResponse *validAPIResponse = [self APIResponseWithCode:200 - responseBody:responseBody]; - - // 2. Stub API Service - // 2.1. Return prepared response. - [self expectAttestAPIRequestWithAttestation:attestation - keyID:keyID - challenge:challenge - response:validAPIResponse - error:nil]; - - // 3. Send request. - __auto_type promise = [self.appAttestAPIService attestKeyWithAttestation:attestation - keyID:keyID - challenge:challenge]; - - // 4. Verify. - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - - XCTAssertTrue(promise.isRejected); - XCTAssertNil(promise.value); - XCTAssertNotNil(promise.error); - - OCMVerifyAll(self.mockAPIService); -} - -#pragma mark - Helpers - -- (GULURLSessionDataResponse *)APIResponseWithCode:(NSInteger)code - responseBody:(NSData *)responseBody { - XCTAssertNotNil(responseBody); - NSHTTPURLResponse *HTTPResponse = [GACURLSessionOCMockStub HTTPResponseWithCode:code]; - GULURLSessionDataResponse *APIResponse = - [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody]; - return APIResponse; -} - -- (void)stubMockAPIServiceRequestForChallengeRequestWithResponse:(id)response { - id URLValidationArg = [self URLValidationArgumentWithCustomMethod:@"generateAppAttestChallenge"]; - OCMStub([self.mockAPIService sendRequestWithURL:URLValidationArg - HTTPMethod:@"POST" - body:nil - additionalHeaders:nil]) - .andDo(^(NSInvocation *invocation) { - XCTAssertFalse([NSThread isMainThread]); - }) - .andReturn([FBLPromise resolvedWith:response]); -} - -/// Returns an OCMock argument constraint for an App Check URL with the specified custom method. -/// -/// The expected URL has the format "{`kBaseURL`}/{`kResourceName`}:{`customMethod`}", for example -/// "https://firebaseappcheck.googleapis.com/v1/projects/project12345/apps/1:12345:ios:hashvalue". -/// -/// @param customMethod The name of the custom action (e.g., "generateAppAttestChallenge") taken -/// on the App Check-protected resource (e.g., for a Firebase app, -/// "projects/project12345/apps/1:12345:ios:hashvalue); see AIP-136 (https://google.aip.dev/136) for -/// more details on custom methods. -- (id)URLValidationArgumentWithCustomMethod:(NSString *)customMethod { - NSString *expectedRequestURL = [NSString - stringWithFormat:@"%@/%@:%@", [self.mockAPIService baseURL], kResourceName, customMethod]; - - id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) { - XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL); - return YES; - }]; - return URLValidationArg; -} - -- (void)expectTokenAPIRequestWithArtifact:(NSData *)attestation - challenge:(NSData *)challenge - assertion:(NSData *)assertion - response:(nullable GULURLSessionDataResponse *)response - error:(nullable NSError *)error { - id URLValidationArg = [self URLValidationArgumentWithCustomMethod:@"exchangeAppAttestAssertion"]; - - id bodyValidationArg = [OCMArg checkWithBlock:^BOOL(NSData *requestBody) { - NSDictionary *decodedData = [NSJSONSerialization JSONObjectWithData:requestBody - options:0 - error:nil]; - - XCTAssert([decodedData isKindOfClass:[NSDictionary class]]); - - // Validate artifact field. - NSString *base64EncodedArtifact = decodedData[@"artifact"]; - XCTAssert([base64EncodedArtifact isKindOfClass:[NSString class]]); - - NSData *decodedAttestation = [[NSData alloc] initWithBase64EncodedString:base64EncodedArtifact - options:0]; - XCTAssertEqualObjects(decodedAttestation, attestation); - - // Validate challenge field. - NSString *base64EncodedChallenge = decodedData[@"challenge"]; - XCTAssert([base64EncodedChallenge isKindOfClass:[NSString class]]); - - NSData *decodedChallenge = [[NSData alloc] initWithBase64EncodedString:base64EncodedChallenge - options:0]; - XCTAssertEqualObjects(decodedChallenge, challenge); - - // Validate assertion field. - NSString *base64EncodedAssertion = decodedData[@"assertion"]; - XCTAssert([base64EncodedAssertion isKindOfClass:[NSString class]]); - - NSData *decodedAssertion = [[NSData alloc] initWithBase64EncodedString:base64EncodedAssertion - options:0]; - XCTAssertEqualObjects(decodedAssertion, assertion); - - return YES; - }]; - - FBLPromise *responsePromise = [FBLPromise pendingPromise]; - if (error) { - [responsePromise reject:error]; - } else { - [responsePromise fulfill:response]; - } - OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg - HTTPMethod:@"POST" - body:bodyValidationArg - additionalHeaders:@{@"Content-Type" : @"application/json"}]) - .andReturn(responsePromise); -} - -- (void)expectTokenWithAPIReponse:(nonnull GULURLSessionDataResponse *)response - toReturnToken:(nullable GACAppCheckToken *)token { - FBLPromise *tokenPromise = [FBLPromise pendingPromise]; - if (token) { - [tokenPromise fulfill:token]; - } else { - NSError *tokenError = [NSError errorWithDomain:self.name code:0 userInfo:nil]; - [tokenPromise reject:tokenError]; - } - OCMExpect([self.mockAPIService appCheckTokenWithAPIResponse:response]).andReturn(tokenPromise); -} - -- (void)expectAttestAPIRequestWithAttestation:(NSData *)attestation - keyID:(NSString *)keyID - challenge:(NSData *)challenge - response:(nullable GULURLSessionDataResponse *)response - error:(nullable NSError *)error { - id URLValidationArg = - [self URLValidationArgumentWithCustomMethod:@"exchangeAppAttestAttestation"]; - - id bodyValidationArg = [OCMArg checkWithBlock:^BOOL(NSData *requestBody) { - NSDictionary *decodedData = [NSJSONSerialization JSONObjectWithData:requestBody - options:0 - error:nil]; - - XCTAssert([decodedData isKindOfClass:[NSDictionary class]]); - - // Validate attestation field. - NSString *base64EncodedAttestation = decodedData[@"attestation_statement"]; - XCTAssert([base64EncodedAttestation isKindOfClass:[NSString class]]); - - NSData *decodedAttestation = - [[NSData alloc] initWithBase64EncodedString:base64EncodedAttestation options:0]; - XCTAssertEqualObjects(decodedAttestation, attestation); - - // Validate challenge field. - NSString *base64EncodedChallenge = decodedData[@"challenge"]; - XCTAssert([base64EncodedAttestation isKindOfClass:[NSString class]]); - - NSData *decodedChallenge = [[NSData alloc] initWithBase64EncodedString:base64EncodedChallenge - options:0]; - XCTAssertEqualObjects(decodedChallenge, challenge); - - // Validate key ID field. - NSString *keyIDField = decodedData[@"key_id"]; - XCTAssert([base64EncodedAttestation isKindOfClass:[NSString class]]); - - XCTAssertEqualObjects(keyIDField, keyID); - - return YES; - }]; - - FBLPromise *resultPromise = [FBLPromise pendingPromise]; - if (error) { - [resultPromise reject:error]; - } else { - [resultPromise fulfill:response]; - } - - OCMExpect([self.mockAPIService sendRequestWithURL:URLValidationArg - HTTPMethod:@"POST" - body:bodyValidationArg - additionalHeaders:@{@"Content-Type" : @"application/json"}]) - .andReturn(resultPromise); -} - -- (NSData *)generateRandomData { - return [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding]; -} - -@end diff --git a/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestProviderTests.m b/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestProviderTests.m deleted file mode 100644 index db46ee36e64b..000000000000 --- a/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestProviderTests.m +++ /dev/null @@ -1,1110 +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 - -#import -#import "FBLPromise+Testing.h" - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppAttestProvider.h" - -#import "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.h" -#import "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h" -#import "AppCheckCore/Sources/AppAttestProvider/GACAppAttestService.h" -#import "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.h" -#import "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.h" -#import "AppCheckCore/Sources/Core/Utils/GACAppCheckCryptoUtils.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -#import "AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h" - -#import "FirebaseCore/Extension/FirebaseCoreInternal.h" - -#import "AppCheckCore/Tests/Utils/AppCheckBackoffWrapperFake/GACAppCheckBackoffWrapperFake.h" - -#if GAC_APP_ATTEST_SUPPORTED_TARGETS - -GAC_APP_ATTEST_PROVIDER_AVAILABILITY -@interface GACAppAttestProvider (Tests) -- (instancetype)initWithAppAttestService:(id)appAttestService - APIService:(id)APIService - keyIDStorage:(id)keyIDStorage - artifactStorage:(id)artifactStorage - backoffWrapper:(id)backoffWrapper; -@end - -GAC_APP_ATTEST_PROVIDER_AVAILABILITY -@interface GACAppAttestProviderTests : XCTestCase - -@property(nonatomic) GACAppAttestProvider *provider; - -@property(nonatomic) OCMockObject *mockAppAttestService; -@property(nonatomic) OCMockObject *mockAPIService; -@property(nonatomic) OCMockObject *mockStorage; -@property(nonatomic) OCMockObject *mockArtifactStorage; - -@property(nonatomic) NSData *randomChallenge; -@property(nonatomic) NSData *randomChallengeHash; - -@property(nonatomic) GACAppCheckBackoffWrapperFake *fakeBackoffWrapper; - -@end - -@implementation GACAppAttestProviderTests - -- (void)setUp { - [super setUp]; - - self.mockAppAttestService = OCMProtocolMock(@protocol(GACAppAttestService)); - self.mockAPIService = OCMProtocolMock(@protocol(GACAppAttestAPIServiceProtocol)); - self.mockStorage = OCMProtocolMock(@protocol(GACAppAttestKeyIDStorageProtocol)); - self.mockArtifactStorage = OCMProtocolMock(@protocol(GACAppAttestArtifactStorageProtocol)); - - self.fakeBackoffWrapper = [[GACAppCheckBackoffWrapperFake alloc] init]; - // Don't backoff by default. - self.fakeBackoffWrapper.isNextOperationAllowed = YES; - - self.provider = [[GACAppAttestProvider alloc] initWithAppAttestService:self.mockAppAttestService - APIService:self.mockAPIService - keyIDStorage:self.mockStorage - artifactStorage:self.mockArtifactStorage - backoffWrapper:self.fakeBackoffWrapper]; - - self.randomChallenge = [@"random challenge" dataUsingEncoding:NSUTF8StringEncoding]; - self.randomChallengeHash = - [[NSData alloc] initWithBase64EncodedString:@"vEq8yE9g+WwfifNqC2wsXN9M3NIDeOKpDBVYLpGbUDY=" - options:0]; -} - -- (void)tearDown { - self.provider = nil; - self.mockArtifactStorage = nil; - self.mockStorage = nil; - self.mockAPIService = nil; - self.mockAppAttestService = nil; - self.fakeBackoffWrapper = nil; -} - -#pragma mark - Init tests - -#if !TARGET_OS_MACCATALYST -// Keychain dependent logic require additional configuration on Catalyst (enabling Keychain -// sharing). For now, keychain dependent tests are disabled for Catalyst. -- (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]; - NSString *resourceName = [GACAppAttestProviderTests resourceNameFromApp:app]; - - XCTAssertNotNil([[GACAppAttestProvider alloc] initWithServiceName:app.name - resourceName:resourceName - baseURL:nil - APIKey:app.options.APIKey - keychainAccessGroup:nil - limitedUse:NO - requestHooks:nil]); -} -#endif // !TARGET_OS_MACCATALYST - -#pragma mark - Initial handshake (attestation) - -- (void)testGetTokenWhenAppAttestIsNotSupported { - NSError *expectedError = - [GACAppCheckErrorUtil unsupportedAttestationProvider:@"AppAttestProvider"]; - - // 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 = ^GACAppCheckBackoffType(NSError *_Nonnull error) { - XCTAssertEqualObjects(error, expectedError); - [errorHandlerExpectation fulfill]; - return GACAppCheckBackoffType1Day; - }; - - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(NO)]; - - // 2. Don't expect other operations. - OCMReject([self.mockStorage getAppAttestKeyID]); - OCMReject([self.mockAppAttestService generateKeyWithCompletionHandler:OCMOCK_ANY]); - OCMReject([self.mockArtifactStorage getArtifactForKey:OCMOCK_ANY]); - OCMReject([self.mockAPIService getRandomChallenge]); - OCMReject([self.mockStorage setAppAttestKeyID:OCMOCK_ANY]); - OCMReject([self.mockAppAttestService attestKey:OCMOCK_ANY - clientDataHash:OCMOCK_ANY - completionHandler:OCMOCK_ANY]); - OCMReject([self.mockAPIService attestKeyWithAttestation:OCMOCK_ANY - keyID:OCMOCK_ANY - challenge:OCMOCK_ANY]); - - // 3. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_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 mocks. - [self verifyAllMocks]; -} - -- (void)testGetToken_WhenNoExistingKey_Success { - // 0. Expect backoff wrapper to be used. - self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"]; - - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - NSError *error = [NSError errorWithDomain:@"testGetToken_WhenNoExistingKey_Success" - code:NSNotFound - userInfo:nil]; - [rejectedPromise reject:error]; - OCMExpect([self.mockStorage getAppAttestKeyID]).andReturn(rejectedPromise); - - // 3. Expect App Attest key to be generated. - NSString *generatedKeyID = @"generatedKeyID"; - id completionArg = [OCMArg invokeBlockWithArgs:generatedKeyID, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService generateKeyWithCompletionHandler:completionArg]); - - // 4. Expect the key ID to be stored. - OCMExpect([self.mockStorage setAppAttestKeyID:generatedKeyID]) - .andReturn([FBLPromise resolvedWith:generatedKeyID]); - - // 5. Expect random challenge to be requested. - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([FBLPromise resolvedWith:self.randomChallenge]); - - // 6. Expect the key to be attested with the challenge. - NSData *attestationData = [@"attestation data" dataUsingEncoding:NSUTF8StringEncoding]; - id attestCompletionArg = [OCMArg invokeBlockWithArgs:attestationData, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService attestKey:generatedKeyID - clientDataHash:self.randomChallengeHash - completionHandler:attestCompletionArg]); - - // 7. Expect key attestation request to be sent. - GACAppCheckToken *FACToken = [[GACAppCheckToken alloc] initWithToken:@"FAC token" - expirationDate:[NSDate date]]; - NSData *artifactData = [@"attestation artifact" dataUsingEncoding:NSUTF8StringEncoding]; - __auto_type attestKeyResponse = - [[GACAppAttestAttestationResponse alloc] initWithArtifact:artifactData token:FACToken]; - OCMExpect([self.mockAPIService attestKeyWithAttestation:attestationData - keyID:generatedKeyID - challenge:self.randomChallenge]) - .andReturn([FBLPromise resolvedWith:attestKeyResponse]); - - // 8. Expect the artifact received from Firebase backend to be saved. - OCMExpect([self.mockArtifactStorage setArtifact:artifactData forKey:generatedKeyID]) - .andReturn([FBLPromise resolvedWith:artifactData]); - - // 9. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertEqualObjects(token.token, FACToken.token); - XCTAssertEqualObjects(token.expirationDate, FACToken.expirationDate); - XCTAssertNil(error); - }]; - - [self waitForExpectations:@[ self.fakeBackoffWrapper.backoffExpectation, completionExpectation ] - timeout:0.5 - enforceOrder:YES]; - - // 10. Verify mocks. - [self verifyAllMocks]; - - // 11. Verify backoff result. - XCTAssertEqualObjects(((GACAppCheckToken *)self.fakeBackoffWrapper.operationResult).token, - FACToken.token); -} - -- (void)testGetToken_WhenExistingUnregisteredKey_Success { - // 0. Expect backoff wrapper to be used. - self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"]; - - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = @"existingKeyID"; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Don't expect App Attest key to be generated. - OCMReject([self.mockAppAttestService generateKeyWithCompletionHandler:OCMOCK_ANY]); - - // 4. Don't expect the key ID to be stored. - OCMReject([self.mockStorage setAppAttestKeyID:OCMOCK_ANY]); - - // 5. Expect a stored artifact to be requested. - __auto_type rejectedPromise = [self rejectedPromiseWithError:[NSError errorWithDomain:self.name - code:NSNotFound - userInfo:nil]]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]).andReturn(rejectedPromise); - - // 6. Expect random challenge to be requested. - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([FBLPromise resolvedWith:self.randomChallenge]); - - // 7. Expect the key to be attested with the challenge. - NSData *attestationData = [@"attestation data" dataUsingEncoding:NSUTF8StringEncoding]; - id attestCompletionArg = [OCMArg invokeBlockWithArgs:attestationData, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService attestKey:existingKeyID - clientDataHash:self.randomChallengeHash - completionHandler:attestCompletionArg]); - - // 8. Expect key attestation request to be sent. - GACAppCheckToken *FACToken = [[GACAppCheckToken alloc] initWithToken:@"FAC token" - expirationDate:[NSDate date]]; - NSData *artifactData = [@"attestation artifact" dataUsingEncoding:NSUTF8StringEncoding]; - __auto_type attestKeyResponse = - [[GACAppAttestAttestationResponse alloc] initWithArtifact:artifactData token:FACToken]; - OCMExpect([self.mockAPIService attestKeyWithAttestation:attestationData - keyID:existingKeyID - challenge:self.randomChallenge]) - .andReturn([FBLPromise resolvedWith:attestKeyResponse]); - - // 9. Expect the artifact received from Firebase backend to be saved. - OCMExpect([self.mockArtifactStorage setArtifact:artifactData forKey:existingKeyID]) - .andReturn([FBLPromise resolvedWith:artifactData]); - - // 10. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertEqualObjects(token.token, FACToken.token); - XCTAssertEqualObjects(token.expirationDate, FACToken.expirationDate); - XCTAssertNil(error); - }]; - - [self waitForExpectations:@[ self.fakeBackoffWrapper.backoffExpectation, completionExpectation ] - timeout:0.5 - enforceOrder:YES]; - - // 11. Verify mocks. - [self verifyAllMocks]; - - // 12. Verify backoff result. - XCTAssertEqualObjects(((GACAppCheckToken *)self.fakeBackoffWrapper.operationResult).token, - FACToken.token); -} - -- (void)testGetToken_WhenUnregisteredKeyAndRandomChallengeError { - // 0. Expect backoff wrapper to be used. - self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"]; - - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = @"existingKeyID"; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Expect a stored artifact to be requested. - __auto_type rejectedPromise = [self rejectedPromiseWithError:[NSError errorWithDomain:self.name - code:NSNotFound - userInfo:nil]]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]).andReturn(rejectedPromise); - - // 4. Expect random challenge to be requested. - NSError *challengeError = [self expectRandomChallengeRequestError]; - - // 5. Don't expect other steps. - OCMReject([self.mockStorage setAppAttestKeyID:OCMOCK_ANY]); - OCMReject([self.mockAppAttestService attestKey:OCMOCK_ANY - clientDataHash:OCMOCK_ANY - completionHandler:OCMOCK_ANY]); - OCMReject([self.mockAPIService attestKeyWithAttestation:OCMOCK_ANY - keyID:OCMOCK_ANY - challenge:OCMOCK_ANY]); - - // 6. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertNil(token); - XCTAssertEqualObjects(error, challengeError); - }]; - - [self waitForExpectations:@[ self.fakeBackoffWrapper.backoffExpectation, completionExpectation ] - timeout:0.5 - enforceOrder:YES]; - - // 7. Verify mocks. - [self verifyAllMocks]; - - // 8. Verify backoff error. - XCTAssertEqualObjects(self.fakeBackoffWrapper.operationError, challengeError); -} - -- (void)testGetToken_WhenUnregisteredKeyAndKeyAttestationError { - // 0. Expect backoff wrapper to be used. - self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"]; - - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = @"existingKeyID"; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Expect a stored artifact to be requested. - __auto_type rejectedPromise = [self rejectedPromiseWithError:[NSError errorWithDomain:self.name - code:NSNotFound - userInfo:nil]]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]).andReturn(rejectedPromise); - - // 4. Expect random challenge to be requested. - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([FBLPromise resolvedWith:self.randomChallenge]); - - // 5. Expect the key to be attested with the challenge. - NSError *attestationError = [NSError errorWithDomain:@"testGetTokenWhenKeyAttestationError" - code:0 - userInfo:nil]; - id attestCompletionArg = [OCMArg invokeBlockWithArgs:[NSNull null], attestationError, nil]; - OCMExpect([self.mockAppAttestService attestKey:existingKeyID - clientDataHash:self.randomChallengeHash - completionHandler:attestCompletionArg]); - - // 6. Don't exchange API request. - OCMReject([self.mockAPIService attestKeyWithAttestation:OCMOCK_ANY - keyID:OCMOCK_ANY - challenge:OCMOCK_ANY]); - - // 7. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertNil(token); - XCTAssertEqualObjects(error, attestationError); - }]; - - [self waitForExpectations:@[ self.fakeBackoffWrapper.backoffExpectation, completionExpectation ] - timeout:0.5 - enforceOrder:YES]; - - // 8. Verify mocks. - [self verifyAllMocks]; - - // 9. Verify backoff error. - XCTAssertEqualObjects(self.fakeBackoffWrapper.operationError, attestationError); -} - -- (void)testGetToken_WhenUnregisteredKeyAndKeyAttestationExchangeError { - // 0. Expect backoff wrapper to be used. - self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"]; - - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = @"existingKeyID"; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Expect a stored artifact to be requested. - __auto_type rejectedPromise = [self rejectedPromiseWithError:[NSError errorWithDomain:self.name - code:NSNotFound - userInfo:nil]]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]).andReturn(rejectedPromise); - - // 4. Expect random challenge to be requested. - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([FBLPromise resolvedWith:self.randomChallenge]); - - // 5. Expect the key to be attested with the challenge. - NSData *attestationData = [@"attestation data" dataUsingEncoding:NSUTF8StringEncoding]; - id attestCompletionArg = [OCMArg invokeBlockWithArgs:attestationData, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService attestKey:existingKeyID - clientDataHash:self.randomChallengeHash - completionHandler:attestCompletionArg]); - - // 6. Expect exchange request to be sent. - NSError *exchangeError = [NSError errorWithDomain:@"testGetTokenWhenKeyAttestationExchangeError" - code:0 - userInfo:nil]; - OCMExpect([self.mockAPIService attestKeyWithAttestation:attestationData - keyID:existingKeyID - challenge:self.randomChallenge]) - .andReturn([self rejectedPromiseWithError:exchangeError]); - - // 7. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertNil(token); - XCTAssertEqualObjects(error, exchangeError); - }]; - - [self waitForExpectations:@[ self.fakeBackoffWrapper.backoffExpectation, completionExpectation ] - timeout:0.5 - enforceOrder:YES]; - - // 8. Verify mocks. - [self verifyAllMocks]; - - // 9. Verify backoff error. - XCTAssertEqualObjects(self.fakeBackoffWrapper.operationError, exchangeError); -} - -#pragma mark Rejected Attestation - -- (void)testGetToken_WhenAttestationIsRejected_ThenAttestationIsResetAndRetriedOnceSuccess { - // 1. Expect App Attest availability to be requested and stored key ID request to fail. - [self expectAppAttestAvailabilityToBeCheckedAndNotExistingStoredKeyRequested]; - - // 2. Expect the App Attest key pair to be generated and attested. - NSString *keyID1 = @"keyID1"; - NSData *attestationData1 = [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding]; - [self expectAppAttestKeyGeneratedAndAttestedWithKeyID:keyID1 attestationData:attestationData1]; - - // 3. Expect exchange request to be sent. - GACAppCheckHTTPError *APIError = [self attestationRejectionHTTPError]; - OCMExpect([self.mockAPIService attestKeyWithAttestation:attestationData1 - keyID:keyID1 - challenge:self.randomChallenge]) - .andReturn([self rejectedPromiseWithError:APIError]); - - // 4. Stored attestation to be reset. - [self expectAttestationReset]; - - // 5. Expect the App Attest key pair to be generated and attested. - NSString *keyID2 = @"keyID2"; - NSData *attestationData2 = [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding]; - [self expectAppAttestKeyGeneratedAndAttestedWithKeyID:keyID2 attestationData:attestationData2]; - - // 6. Expect exchange request to be sent. - GACAppCheckToken *FACToken = [[GACAppCheckToken alloc] initWithToken:@"FAC token" - expirationDate:[NSDate date]]; - NSData *artifactData = [@"attestation artifact" dataUsingEncoding:NSUTF8StringEncoding]; - __auto_type attestKeyResponse = - [[GACAppAttestAttestationResponse alloc] initWithArtifact:artifactData token:FACToken]; - OCMExpect([self.mockAPIService attestKeyWithAttestation:attestationData2 - keyID:keyID2 - challenge:self.randomChallenge]) - .andReturn([FBLPromise resolvedWith:attestKeyResponse]); - - // 7. Expect the artifact received from Firebase backend to be saved. - OCMExpect([self.mockArtifactStorage setArtifact:artifactData forKey:keyID2]) - .andReturn([FBLPromise resolvedWith:artifactData]); - - // 8. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertEqualObjects(token.token, FACToken.token); - XCTAssertEqualObjects(token.expirationDate, FACToken.expirationDate); - XCTAssertNil(error); - }]; - - [self waitForExpectations:@[ completionExpectation ] timeout:0.5 enforceOrder:YES]; - - // 8. Verify mocks. - [self verifyAllMocks]; -} - -- (void)testGetToken_WhenAttestationIsRejected_ThenAttestationIsResetAndRetriedOnceError { - // 1. Expect App Attest availability to be requested and stored key ID request to fail. - [self expectAppAttestAvailabilityToBeCheckedAndNotExistingStoredKeyRequested]; - - // 2. Expect the App Attest key pair to be generated and attested. - NSString *keyID1 = @"keyID1"; - NSData *attestationData1 = [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding]; - [self expectAppAttestKeyGeneratedAndAttestedWithKeyID:keyID1 attestationData:attestationData1]; - - // 3. Expect exchange request to be sent. - GACAppCheckHTTPError *APIError = [self attestationRejectionHTTPError]; - OCMExpect([self.mockAPIService attestKeyWithAttestation:attestationData1 - keyID:keyID1 - challenge:self.randomChallenge]) - .andReturn([self rejectedPromiseWithError:APIError]); - - // 4. Stored attestation to be reset. - [self expectAttestationReset]; - - // 5. Expect the App Attest key pair to be generated and attested. - NSString *keyID2 = @"keyID2"; - NSData *attestationData2 = [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding]; - [self expectAppAttestKeyGeneratedAndAttestedWithKeyID:keyID2 attestationData:attestationData2]; - - // 6. Expect exchange request to be sent. - OCMExpect([self.mockAPIService attestKeyWithAttestation:attestationData2 - keyID:keyID2 - challenge:self.randomChallenge]) - .andReturn([self rejectedPromiseWithError:APIError]); - - // 7. Stored attestation to be reset. - [self expectAttestationReset]; - - // 8. Don't expect the artifact received from Firebase backend to be saved. - OCMReject([self.mockArtifactStorage setArtifact:OCMOCK_ANY forKey:OCMOCK_ANY]); - - // 9. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertNil(token); - GACAppAttestRejectionError *expectedError = [[GACAppAttestRejectionError alloc] init]; - XCTAssertEqualObjects(error, expectedError); - }]; - - [self waitForExpectations:@[ completionExpectation ] timeout:0.5 enforceOrder:YES]; - - // 9. Verify mocks. - [self verifyAllMocks]; -} - -#pragma mark - FAC token refresh (assertion) - -- (void)testGetToken_WhenKeyRegistered_Success { - [self assertGetToken_WhenKeyRegistered_Success]; -} - -- (void)testGetToken_WhenKeyRegisteredAndChallengeRequestError { - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = @"existingKeyID"; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Expect a stored artifact to be requested. - NSData *storedArtifact = [@"storedArtifact" dataUsingEncoding:NSUTF8StringEncoding]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]) - .andReturn([FBLPromise resolvedWith:storedArtifact]); - - // 4. Expect random challenge to be requested. - NSError *challengeError = [self expectRandomChallengeRequestError]; - - // 5. Don't expect assertion to be requested. - OCMReject([self.mockAppAttestService generateAssertion:OCMOCK_ANY - clientDataHash:OCMOCK_ANY - completionHandler:OCMOCK_ANY]); - - // 6. Don't expect assertion request to be sent. - OCMReject([self.mockAPIService getAppCheckTokenWithArtifact:OCMOCK_ANY - challenge:OCMOCK_ANY - assertion:OCMOCK_ANY]); - - // 7. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertNil(token); - XCTAssertEqualObjects(error, challengeError); - }]; - - [self waitForExpectations:@[ completionExpectation ] timeout:0.5]; - - // 8. Verify mocks. - [self verifyAllMocks]; -} - -- (void)testGetToken_WhenKeyRegisteredAndGenerateAssertionError { - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = @"existingKeyID"; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Expect a stored artifact to be requested. - NSData *storedArtifact = [@"storedArtifact" dataUsingEncoding:NSUTF8StringEncoding]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]) - .andReturn([FBLPromise resolvedWith:storedArtifact]); - - // 4. Expect random challenge to be requested. - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([FBLPromise resolvedWith:self.randomChallenge]); - - // 5. Don't expect assertion to be requested. - NSError *generateAssertionError = - [NSError errorWithDomain:@"testGetToken_WhenKeyRegisteredAndGenerateAssertionError" - code:0 - userInfo:nil]; - id completionBlockArg = [OCMArg invokeBlockWithArgs:[NSNull null], generateAssertionError, nil]; - OCMExpect([self.mockAppAttestService - generateAssertion:existingKeyID - clientDataHash:[self dataHashForAssertionWithArtifactData:storedArtifact] - completionHandler:completionBlockArg]); - - // 6. Don't expect assertion request to be sent. - OCMReject([self.mockAPIService getAppCheckTokenWithArtifact:OCMOCK_ANY - challenge:OCMOCK_ANY - assertion:OCMOCK_ANY]); - - // 7. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertNil(token); - XCTAssertEqualObjects(error, generateAssertionError); - }]; - - [self waitForExpectations:@[ completionExpectation ] timeout:0.5]; - - // 8. Verify mocks. - [self verifyAllMocks]; -} - -- (void)testGetToken_WhenKeyRegisteredAndTokenExchangeRequestError { - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = @"existingKeyID"; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Expect a stored artifact to be requested. - NSData *storedArtifact = [@"storedArtifact" dataUsingEncoding:NSUTF8StringEncoding]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]) - .andReturn([FBLPromise resolvedWith:storedArtifact]); - - // 4. Expect random challenge to be requested. - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([FBLPromise resolvedWith:self.randomChallenge]); - - // 5. Don't expect assertion to be requested. - NSData *assertion = [@"generatedAssertion" dataUsingEncoding:NSUTF8StringEncoding]; - id completionBlockArg = [OCMArg invokeBlockWithArgs:assertion, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService - generateAssertion:existingKeyID - clientDataHash:[self dataHashForAssertionWithArtifactData:storedArtifact] - completionHandler:completionBlockArg]); - - // 6. Expect assertion request to be sent. - NSError *tokenExchangeError = - [NSError errorWithDomain:@"testGetToken_WhenKeyRegisteredAndTokenExchangeRequestError" - code:0 - userInfo:nil]; - OCMExpect([self.mockAPIService getAppCheckTokenWithArtifact:storedArtifact - challenge:self.randomChallenge - assertion:assertion]) - .andReturn([self rejectedPromiseWithError:tokenExchangeError]); - - // 7. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertNil(token); - XCTAssertEqualObjects(error, tokenExchangeError); - }]; - - [self waitForExpectations:@[ completionExpectation ] timeout:0.5]; - - // 8. Verify mocks. - [self verifyAllMocks]; -} - -#pragma mark - Request merging - -- (void)testGetToken_WhenCalledSeveralTimesSuccess_ThenThereIsOnlyOneOngoingHandshake { - // 0. Expect backoff wrapper to be used only once. - self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"]; - - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = @"existingKeyID"; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Expect a stored artifact to be requested. - NSData *storedArtifact = [@"storedArtifact" dataUsingEncoding:NSUTF8StringEncoding]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]) - .andReturn([FBLPromise resolvedWith:storedArtifact]); - - // 4. Expect random challenge to be requested. - // 4.1. Create a pending promise to fulfill later. - FBLPromise *challengeRequestPromise = [FBLPromise pendingPromise]; - // 4.2. Stub getRandomChallenge method. - OCMExpect([self.mockAPIService getRandomChallenge]).andReturn(challengeRequestPromise); - - // 5. Expect assertion to be requested. - NSData *assertion = [@"generatedAssertion" dataUsingEncoding:NSUTF8StringEncoding]; - id completionBlockArg = [OCMArg invokeBlockWithArgs:assertion, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService - generateAssertion:existingKeyID - clientDataHash:[self dataHashForAssertionWithArtifactData:storedArtifact] - completionHandler:completionBlockArg]); - - // 6. Expect assertion request to be sent. - GACAppCheckToken *FACToken = [[GACAppCheckToken alloc] initWithToken:@"FAC token" - expirationDate:[NSDate date]]; - OCMExpect([self.mockAPIService getAppCheckTokenWithArtifact:storedArtifact - challenge:self.randomChallenge - assertion:assertion]) - .andReturn([FBLPromise resolvedWith:FACToken]); - - // 7. Call get token several times. - NSInteger callsCount = 10; - NSMutableArray *completionExpectations = [NSMutableArray arrayWithCapacity:callsCount]; - - for (NSInteger i = 0; i < callsCount; i++) { - // 7.1 Expect the completion to be called for each get token method called. - XCTestExpectation *completionExpectation = [self - expectationWithDescription:[NSString stringWithFormat:@"completionExpectation%@", @(i)]]; - [completionExpectations addObject:completionExpectation]; - - // 7.2. Call get token. - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertEqualObjects(token.token, FACToken.token); - XCTAssertEqualObjects(token.expirationDate, FACToken.expirationDate); - XCTAssertNil(error); - }]; - } - - // 7.3. Resolve get challenge promise to finish the operation. - [challengeRequestPromise fulfill:self.randomChallenge]; - - // 7.4. Wait for all completions to be called. - NSArray *expectations = - [completionExpectations arrayByAddingObject:self.fakeBackoffWrapper.backoffExpectation]; - [self waitForExpectations:expectations timeout:1]; - - // 8. Verify mocks. - [self verifyAllMocks]; - - // 9. Check another get token call after. - [self assertGetToken_WhenKeyRegistered_Success]; -} - -- (void)testGetToken_WhenCalledSeveralTimesError_ThenThereIsOnlyOneOngoingHandshake { - // 0. Expect backoff wrapper to be used only once. - self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"]; - - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = @"existingKeyID"; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Expect a stored artifact to be requested. - NSData *storedArtifact = [@"storedArtifact" dataUsingEncoding:NSUTF8StringEncoding]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]) - .andReturn([FBLPromise resolvedWith:storedArtifact]); - - // 4. Expect random challenge to be requested. - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([FBLPromise resolvedWith:self.randomChallenge]); - - // 5. Don't expect assertion to be requested. - NSData *assertion = [@"generatedAssertion" dataUsingEncoding:NSUTF8StringEncoding]; - id completionBlockArg = [OCMArg invokeBlockWithArgs:assertion, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService - generateAssertion:existingKeyID - clientDataHash:[self dataHashForAssertionWithArtifactData:storedArtifact] - completionHandler:completionBlockArg]); - - // 6. Expect assertion request to be sent. - // 6.1. Create a pending promise to reject later. - FBLPromise *assertionRequestPromise = [FBLPromise pendingPromise]; - // 6.2. Stub assertion request. - OCMExpect([self.mockAPIService getAppCheckTokenWithArtifact:storedArtifact - challenge:self.randomChallenge - assertion:assertion]) - .andReturn(assertionRequestPromise); - // 6.3. Create an expected error to be rejected with later. - NSError *assertionRequestError = [NSError errorWithDomain:self.name code:0 userInfo:nil]; - - // 7. Call get token several times. - NSInteger callsCount = 10; - NSMutableArray *completionExpectations = [NSMutableArray arrayWithCapacity:callsCount]; - - for (NSInteger i = 0; i < callsCount; i++) { - // 7.1 Expect the completion to be called for each get token method called. - XCTestExpectation *completionExpectation = [self - expectationWithDescription:[NSString stringWithFormat:@"completionExpectation%@", @(i)]]; - [completionExpectations addObject:completionExpectation]; - - // 7.2. Call get token. - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertEqualObjects(error, assertionRequestError); - XCTAssertNil(token); - }]; - } - - // 7.3. Reject get challenge promise to finish the operation. - [assertionRequestPromise reject:assertionRequestError]; - - // 7.4. Wait for all completions to be called. - NSArray *expectations = - [completionExpectations arrayByAddingObject:self.fakeBackoffWrapper.backoffExpectation]; - [self waitForExpectations:expectations timeout:1]; - - // 8. Verify mocks. - [self verifyAllMocks]; - - // 9. Check another get token call after. - [self assertGetToken_WhenKeyRegistered_Success]; -} - -#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.mockAppAttestService isSupported]); - OCMReject([self.mockStorage getAppAttestKeyID]); - OCMReject([self.mockAppAttestService generateKeyWithCompletionHandler:OCMOCK_ANY]); - OCMReject([self.mockArtifactStorage getArtifactForKey:OCMOCK_ANY]); - OCMReject([self.mockAPIService getRandomChallenge]); - OCMReject([self.mockStorage setAppAttestKeyID:OCMOCK_ANY]); - OCMReject([self.mockAppAttestService attestKey:OCMOCK_ANY - clientDataHash:OCMOCK_ANY - completionHandler:OCMOCK_ANY]); - OCMReject([self.mockAPIService attestKeyWithAttestation:OCMOCK_ANY - keyID:OCMOCK_ANY - challenge:OCMOCK_ANY]); - - // 3. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_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 mocks. - [self verifyAllMocks]; -} - -#pragma mark - Helpers - -- (NSData *)dataHashForAssertionWithArtifactData:(NSData *)artifact { - NSMutableData *statement = [artifact mutableCopy]; - [statement appendData:self.randomChallenge]; - return [GACAppCheckCryptoUtils sha256HashFromData:statement]; -} - -- (FBLPromise *)rejectedPromiseWithError:(NSError *)error { - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise reject:error]; - return rejectedPromise; -} - -- (NSError *)expectRandomChallengeRequestError { - NSError *challengeError = [NSError errorWithDomain:@"testGetToken_WhenRandomChallengeError" - code:NSNotFound - userInfo:nil]; - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([self rejectedPromiseWithError:challengeError]); - return challengeError; -} - -- (void)verifyAllMocks { - OCMVerifyAll(self.mockAppAttestService); - OCMVerifyAll(self.mockAPIService); - OCMVerifyAll(self.mockStorage); - OCMVerifyAll(self.mockArtifactStorage); -} - -- (GACAppCheckHTTPError *)attestationRejectionHTTPError { - NSHTTPURLResponse *response = - [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"http://localhost"] - statusCode:403 - HTTPVersion:@"HTTP/1.1" - headerFields:nil]; - NSData *responseBody = [@"Could not verify attestation" dataUsingEncoding:NSUTF8StringEncoding]; - return [[GACAppCheckHTTPError alloc] initWithHTTPResponse:response data:responseBody]; -} - -- (void)assertGetToken_WhenKeyRegistered_Success { - // 0. Expect backoff wrapper to be used. - self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"]; - - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - NSString *existingKeyID = [NSUUID UUID].UUIDString; - OCMExpect([self.mockStorage getAppAttestKeyID]) - .andReturn([FBLPromise resolvedWith:existingKeyID]); - - // 3. Expect a stored artifact to be requested. - NSData *storedArtifact = [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding]; - OCMExpect([self.mockArtifactStorage getArtifactForKey:existingKeyID]) - .andReturn([FBLPromise resolvedWith:storedArtifact]); - - // 4. Expect random challenge to be requested. - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([FBLPromise resolvedWith:self.randomChallenge]); - - // 5. Expect assertion to be requested. - NSData *assertion = [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding]; - id completionBlockArg = [OCMArg invokeBlockWithArgs:assertion, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService - generateAssertion:existingKeyID - clientDataHash:[self dataHashForAssertionWithArtifactData:storedArtifact] - completionHandler:completionBlockArg]); - - // 6. Expect assertion request to be sent. - GACAppCheckToken *FACToken = [[GACAppCheckToken alloc] initWithToken:[NSUUID UUID].UUIDString - expirationDate:[NSDate date]]; - OCMExpect([self.mockAPIService getAppCheckTokenWithArtifact:storedArtifact - challenge:self.randomChallenge - assertion:assertion]) - .andReturn([FBLPromise resolvedWith:FACToken]); - - // 7. Call get token. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - - XCTAssertEqualObjects(token.token, FACToken.token); - XCTAssertEqualObjects(token.expirationDate, FACToken.expirationDate); - XCTAssertNil(error); - }]; - - [self waitForExpectations:@[ self.fakeBackoffWrapper.backoffExpectation, completionExpectation ] - timeout:0.5]; - - // 8. Verify mocks. - [self verifyAllMocks]; - - // 9. Verify backoff result. - XCTAssertEqualObjects(((GACAppCheckToken *)self.fakeBackoffWrapper.operationResult).token, - FACToken.token); -} - -- (void)expectAppAttestAvailabilityToBeCheckedAndNotExistingStoredKeyRequested { - // 1. Expect GACAppAttestService.isSupported. - [OCMExpect([self.mockAppAttestService isSupported]) andReturnValue:@(YES)]; - - // 2. Expect storage getAppAttestKeyID. - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - NSError *error = [NSError errorWithDomain:@"testGetToken_WhenNoExistingKey_Success" - code:NSNotFound - userInfo:nil]; - [rejectedPromise reject:error]; - OCMExpect([self.mockStorage getAppAttestKeyID]).andReturn(rejectedPromise); -} - -- (void)expectAppAttestKeyGeneratedAndAttestedWithKeyID:(NSString *)keyID - attestationData:(NSData *)attestationData { - // 1. Expect App Attest key to be generated. - id completionArg = [OCMArg invokeBlockWithArgs:keyID, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService generateKeyWithCompletionHandler:completionArg]); - - // 2. Expect the key ID to be stored. - OCMExpect([self.mockStorage setAppAttestKeyID:keyID]).andReturn([FBLPromise resolvedWith:keyID]); - - // 3. Expect random challenge to be requested. - OCMExpect([self.mockAPIService getRandomChallenge]) - .andReturn([FBLPromise resolvedWith:self.randomChallenge]); - - // 4. Expect the key to be attested with the challenge. - id attestCompletionArg = [OCMArg invokeBlockWithArgs:attestationData, [NSNull null], nil]; - OCMExpect([self.mockAppAttestService attestKey:keyID - clientDataHash:self.randomChallengeHash - completionHandler:attestCompletionArg]); -} - -- (void)expectAttestationReset { - // 1. Expect stored key ID to be reset. - OCMExpect([self.mockStorage setAppAttestKeyID:nil]).andReturn([FBLPromise resolvedWith:nil]); - - // 2. Expect stored attestation artifact to be reset. - OCMExpect([self.mockArtifactStorage setArtifact:nil forKey:@""]) - .andReturn([FBLPromise resolvedWith:nil]); -} - -// TODO(andrewheard): Remove from generic App Check SDK. -// FIREBASE_APP_CHECK_ONLY_BEGIN - -+ (NSString *)resourceNameFromApp:(FIRApp *)app { - return [NSString - stringWithFormat:@"projects/%@/apps/%@", app.options.projectID, app.options.googleAppID]; -} - -// FIREBASE_APP_CHECK_ONLY_END - -@end - -#endif // GAC_APP_ATTEST_SUPPORTED_TARGETS diff --git a/AppCheckCore/Tests/Unit/AppAttestProvider/Storage/GACAppAttestArtifactStorageTests.m b/AppCheckCore/Tests/Unit/AppAttestProvider/Storage/GACAppAttestArtifactStorageTests.m deleted file mode 100644 index 2951a904f34e..000000000000 --- a/AppCheckCore/Tests/Unit/AppAttestProvider/Storage/GACAppAttestArtifactStorageTests.m +++ /dev/null @@ -1,302 +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 - -// 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 - -#import - -#import - -#import "FBLPromise+Testing.h" - -#import "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" - -static NSString *const kAppName = @"GACAppAttestArtifactStorageTests"; -static NSString *const kAppID = @"1:100000000000:ios:aaaaaaaaaaaaaaaaaaaaaaaa"; - -static NSString *const kAppName = @"GACAppAttestArtifactStorageTests"; -static NSString *const kAppID = @"1:100000000000:ios:aaaaaaaaaaaaaaaaaaaaaaaa"; - -@interface GACAppAttestArtifactStorageTests : XCTestCase - -@property(nonatomic) NSString *keySuffix; -@property(nonatomic) GACAppAttestArtifactStorage *storage; - -@end - -@implementation GACAppAttestArtifactStorageTests - -- (void)setUp { - [super setUp]; - - self.keySuffix = [GACAppAttestArtifactStorageTests artifactKeySuffixForAppName:kAppName - appID:kAppID]; - - self.storage = [[GACAppAttestArtifactStorage alloc] initWithKeySuffix:self.keySuffix - accessGroup:nil]; -} - -- (void)tearDown { - self.storage = nil; - [super tearDown]; -} - -- (void)testSetAndGetArtifact { - [self assertSetGetForStorage]; -} - -- (void)testRemoveArtifact { - NSString *keyID = [NSUUID UUID].UUIDString; - - // 1. Save an artifact to storage and check it is stored. - [self assertSetGetForStorage]; - - // 2. Remove artifact. - __auto_type setPromise = [self.storage setArtifact:nil forKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNil(setPromise.value); - XCTAssertNil(setPromise.error); - - // 3. Check it has been removed. - __auto_type getPromise = [self.storage getArtifactForKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNil(getPromise.value); - XCTAssertNil(getPromise.error); -} - -- (void)testSetAndGetPerApp { - // Assert storages for apps with the same name can independently set/get artifact. - [self assertIndependentSetGetForStoragesWithAppName1:kAppName - appID1:@"app_id_1" - appName2:kAppName - appID2:@"app_id_2"]; - // Assert storages for apps with the same app ID can independently set/get artifact. - [self assertIndependentSetGetForStoragesWithAppName1:@"app_1" - appID1:kAppID - appName2:@"app_2" - appID2:kAppID]; - // Assert storages for apps with different info can independently set/get artifact. - [self assertIndependentSetGetForStoragesWithAppName1:@"app_1" - appID1:@"app_id_1" - appName2:@"app_2" - appID2:@"app_id_2"]; -} - -- (void)testSetArtifactForOneKeyGetForAnotherKey { - // Set an artifact for a key. - [self assertSetGetForStorage]; - - // Try to get artifact for a different key. - NSString *keyID = [NSUUID UUID].UUIDString; - __auto_type getPromise = [self.storage getArtifactForKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNil(getPromise.value); - XCTAssertNil(getPromise.error); -} - -- (void)testSetArtifactForNewKeyRemovesArtifactForOldKey { - // 1. Store an artifact. - NSString *oldKeyID = [self assertSetGetForStorage]; - - // 2. Replace the artifact. - NSString *newKeyID = [self assertSetGetForStorage]; - XCTAssertNotEqualObjects(oldKeyID, newKeyID); - - // 3. Check old artifact was removed. - __auto_type getPromise = [self.storage getArtifactForKey:oldKeyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNil(getPromise.value); - XCTAssertNil(getPromise.error); -} - -- (void)testGetArtifact_KeychainError { - // 1. Set up storage mock. - id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]); - GACAppAttestArtifactStorage *artifactStorage = - [[GACAppAttestArtifactStorage alloc] initWithKeySuffix:self.keySuffix - keychainStorage:mockKeychainStorage - accessGroup:nil]; - - // 2. Create and expect keychain error. - NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil]; - OCMExpect([mockKeychainStorage getObjectForKey:[OCMArg any] - objectClass:[OCMArg any] - accessGroup:[OCMArg any]]) - .andReturn([FBLPromise resolvedWith:gulsKeychainError]); - - // 3. Get artifact and verify results. - __auto_type getPromise = [artifactStorage getArtifactForKey:@"key"]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNotNil(getPromise.error); - XCTAssertEqualObjects(getPromise.error, - [GACAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]); - - // 4. Verify storage mock. - OCMVerifyAll(mockKeychainStorage); -} - -- (void)testSetArtifact_KeychainError { - // 1. Set up storage mock. - id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]); - GACAppAttestArtifactStorage *artifactStorage = - [[GACAppAttestArtifactStorage alloc] initWithKeySuffix:self.keySuffix - keychainStorage:mockKeychainStorage - accessGroup:nil]; - // 2. Create and expect keychain error. - NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil]; - OCMExpect([mockKeychainStorage setObject:[OCMArg any] - forKey:[OCMArg any] - accessGroup:[OCMArg any]]) - .andReturn([FBLPromise resolvedWith:gulsKeychainError]); - - // 3. Set artifact and verify results. - NSData *artifact = [@"artifact" dataUsingEncoding:NSUTF8StringEncoding]; - __auto_type setPromise = [artifactStorage setArtifact:artifact forKey:@"key"]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNotNil(setPromise.error); - XCTAssertEqualObjects(setPromise.error, - [GACAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]); - - // 4. Verify storage mock. - OCMVerifyAll(mockKeychainStorage); -} - -- (void)testRemoveArtifact_KeychainError { - // 1. Set up storage mock. - id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]); - GACAppAttestArtifactStorage *artifactStorage = - [[GACAppAttestArtifactStorage alloc] initWithKeySuffix:self.keySuffix - keychainStorage:mockKeychainStorage - accessGroup:nil]; - - // 2. Create and expect keychain error. - NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil]; - OCMExpect([mockKeychainStorage removeObjectForKey:[OCMArg any] accessGroup:[OCMArg any]]) - .andReturn([FBLPromise resolvedWith:gulsKeychainError]); - - // 3. Remove artifact and verify results. - __auto_type setPromise = [artifactStorage setArtifact:nil forKey:@"key"]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNotNil(setPromise.error); - XCTAssertEqualObjects(setPromise.error, - [GACAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]); - - // 4. Verify storage mock. - OCMVerifyAll(mockKeychainStorage); -} - -#pragma mark - Helpers - -/// Sets a random artifact for a random key and asserts it can be read. -/// @return The random key ID used to set and get the artifact. -- (NSString *)assertSetGetForStorage { - NSData *artifactToSet = [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding]; - NSString *keyID = [NSUUID UUID].UUIDString; - - __auto_type setPromise = [self.storage setArtifact:artifactToSet forKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise.value, artifactToSet); - XCTAssertNil(setPromise.error); - - __auto_type getPromise = [self.storage getArtifactForKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(getPromise.value, artifactToSet); - XCTAssertNil(getPromise.error); - - __weak __auto_type weakSelf = self; - [self addTeardownBlock:^{ - // Cleanup storage. - [weakSelf.storage setArtifact:nil forKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - }]; - - return keyID; -} - -- (void)assertIndependentSetGetForStoragesWithAppName1:(NSString *)appName1 - appID1:(NSString *)appID1 - appName2:(NSString *)appName2 - appID2:(NSString *)appID2 { - NSString *keyID = [NSUUID UUID].UUIDString; - NSString *keySuffix1 = [GACAppAttestArtifactStorageTests artifactKeySuffixForAppName:appName1 - appID:appID1]; - NSString *keySuffix2 = [GACAppAttestArtifactStorageTests artifactKeySuffixForAppName:appName2 - appID:appID2]; - - // Create two storages. - GACAppAttestArtifactStorage *storage1 = - [[GACAppAttestArtifactStorage alloc] initWithKeySuffix:keySuffix1 accessGroup:nil]; - GACAppAttestArtifactStorage *storage2 = - [[GACAppAttestArtifactStorage alloc] initWithKeySuffix:keySuffix2 accessGroup:nil]; - // 1. Independently set artifacts for the two storages. - NSData *artifact1 = [@"app_attest_artifact1" dataUsingEncoding:NSUTF8StringEncoding]; - FBLPromise *setPromise1 = [storage1 setArtifact:artifact1 forKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise1.value, artifact1); - XCTAssertNil(setPromise1.error); - - NSData *artifact2 = [@"app_attest_artifact2" dataUsingEncoding:NSUTF8StringEncoding]; - __auto_type setPromise2 = [storage2 setArtifact:artifact2 forKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise2.value, artifact2); - XCTAssertNil(setPromise2.error); - - // 2. Get artifacts for the two storages. - __auto_type getPromise1 = [storage1 getArtifactForKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(getPromise1.value, artifact1); - XCTAssertNil(getPromise1.error); - - __auto_type getPromise2 = [storage2 getArtifactForKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(getPromise2.value, artifact2); - XCTAssertNil(getPromise2.error); - - // 3. Assert that artifacts were set and retrieved independently of one another. - XCTAssertNotEqualObjects(getPromise1.value, getPromise2.value); - - // Cleanup storages. - [storage1 setArtifact:nil forKey:keyID]; - [storage2 setArtifact:nil forKey:keyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); -} - -// TODO(andrewheard): Remove from generic App Check SDK. -// FIREBASE_APP_CHECK_ONLY_BEGIN - -+ (NSString *)artifactKeySuffixForAppName:(NSString *)appName appID:(NSString *)appID { - return [NSString stringWithFormat:@"%@.%@", appName, appID]; -} - -// FIREBASE_APP_CHECK_ONLY_END - -@end - -#endif // !TARGET_OS_MACCATALYST && !TARGET_OS_OSX - -#endif // !SWIFT_PACKAGE diff --git a/AppCheckCore/Tests/Unit/AppAttestProvider/Storage/GACAppAttestKeyIDStorageTests.m b/AppCheckCore/Tests/Unit/AppAttestProvider/Storage/GACAppAttestKeyIDStorageTests.m deleted file mode 100644 index c404c4b2547f..000000000000 --- a/AppCheckCore/Tests/Unit/AppAttestProvider/Storage/GACAppAttestKeyIDStorageTests.m +++ /dev/null @@ -1,160 +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 - -#import "FBLPromise+Testing.h" - -#import "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.h" - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" - -static NSString *const kAppName = @"GACAppAttestKeyIDStorageTestsApp"; -static NSString *const kAppID = @"app_id"; - -@interface GACAppAttestKeyIDStorageTests : XCTestCase -@property(nonatomic) NSString *keySuffix; -@property(nonatomic) GACAppAttestKeyIDStorage *storage; -@end - -@implementation GACAppAttestKeyIDStorageTests - -- (void)setUp { - [super setUp]; - - self.keySuffix = [NSString stringWithFormat:@"%@.%@", kAppName, kAppID]; - self.storage = [[GACAppAttestKeyIDStorage alloc] initWithKeySuffix:self.keySuffix]; -} - -- (void)tearDown { - // Remove the app attest key ID from storage. - [self.storage setAppAttestKeyID:nil]; - FBLWaitForPromisesWithTimeout(1.0); - self.storage = nil; - - [super tearDown]; -} - -- (void)testInitWithApp { - XCTAssertNotNil([[GACAppAttestKeyIDStorage alloc] initWithKeySuffix:self.keySuffix]); -} - -- (void)testSetAndGetAppAttestKeyID { - NSString *appAttestKeyID = @"app_attest_key_ID"; - - FBLPromise *setPromise = [self.storage setAppAttestKeyID:appAttestKeyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise.value, appAttestKeyID); - XCTAssertNil(setPromise.error); - - __auto_type getPromise = [self.storage getAppAttestKeyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(getPromise.value, appAttestKeyID); - XCTAssertNil(getPromise.error); -} - -- (void)testRemoveAppAttestKeyID { - FBLPromise *setPromise = [self.storage setAppAttestKeyID:nil]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise.value, nil); - XCTAssertNil(setPromise.error); -} - -- (void)testGetAppAttestKeyID_WhenAppAttestKeyIDNotFoundError { - __auto_type getPromise = [self.storage getAppAttestKeyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNotNil(getPromise.error); - XCTAssertEqualObjects(getPromise.error, [GACAppCheckErrorUtil appAttestKeyIDNotFound]); -} - -- (void)testSetGetAppAttestKeyIDPerApp { - // Assert storages for apps with the same name can independently set/get app attest key ID. - [self assertIndependentSetGetForStoragesWithAppName1:kAppName - appID1:@"app_id_1" - appName2:kAppName - appID2:@"app_id_2"]; - // Assert storages for apps with the same app ID can independently set/get app attest key ID. - [self assertIndependentSetGetForStoragesWithAppName1:@"app_1" - appID1:kAppID - appName2:@"app_2" - appID2:kAppID]; - // Assert storages for apps with different info can independently set/get app attest key ID. - [self assertIndependentSetGetForStoragesWithAppName1:@"app_1" - appID1:@"app_id_1" - appName2:@"app_2" - appID2:@"app_id_2"]; -} - -#pragma mark - Helpers - -- (void)assertIndependentSetGetForStoragesWithAppName1:(NSString *)appName1 - appID1:(NSString *)appID1 - appName2:(NSString *)appName2 - appID2:(NSString *)appID2 { - NSString *keySuffix1 = [GACAppAttestKeyIDStorageTests storageKeySuffixForAppName:appName1 - appID:appID1]; - NSString *keySuffix2 = [GACAppAttestKeyIDStorageTests storageKeySuffixForAppName:appName2 - appID:appID2]; - - // Create two storages. - GACAppAttestKeyIDStorage *storage1 = - [[GACAppAttestKeyIDStorage alloc] initWithKeySuffix:keySuffix1]; - GACAppAttestKeyIDStorage *storage2 = - [[GACAppAttestKeyIDStorage alloc] initWithKeySuffix:keySuffix2]; - // 1. Independently set app attest key IDs for the two storages. - NSString *appAttestKeyID1 = @"app_attest_key_ID1"; - FBLPromise *setPromise1 = [storage1 setAppAttestKeyID:appAttestKeyID1]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise1.value, appAttestKeyID1); - XCTAssertNil(setPromise1.error); - - NSString *appAttestKeyID2 = @"app_attest_key_ID2"; - __auto_type setPromise2 = [storage2 setAppAttestKeyID:appAttestKeyID2]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise2.value, appAttestKeyID2); - XCTAssertNil(setPromise2.error); - - // 2. Get app attest key IDs for the two storages. - __auto_type getPromise1 = [storage1 getAppAttestKeyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(getPromise1.value, appAttestKeyID1); - XCTAssertNil(getPromise1.error); - - __auto_type getPromise2 = [storage2 getAppAttestKeyID]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(getPromise2.value, appAttestKeyID2); - XCTAssertNil(getPromise2.error); - - // 3. Assert that the app attest key IDs were set and retrieved independently of one another. - XCTAssertNotEqualObjects(getPromise1.value, getPromise2.value); - - // Cleanup storages. - [storage1 setAppAttestKeyID:nil]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - [storage2 setAppAttestKeyID:nil]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); -} - -// TODO(andrewheard): Remove from generic App Check SDK. -// FIREBASE_APP_CHECK_ONLY_BEGIN - -+ (NSString *)storageKeySuffixForAppName:(NSString *)appName appID:(NSString *)appID { - return [NSString stringWithFormat:@"%@.%@", appName, appID]; -} - -// FIREBASE_APP_CHECK_ONLY_END - -@end diff --git a/AppCheckCore/Tests/Unit/Core/GACAppCheckStorageTests.m b/AppCheckCore/Tests/Unit/Core/GACAppCheckStorageTests.m deleted file mode 100644 index 2725f51d21b5..000000000000 --- a/AppCheckCore/Tests/Unit/Core/GACAppCheckStorageTests.m +++ /dev/null @@ -1,200 +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 - -// 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 - -#import - -#import - -#import "FBLPromise+Testing.h" - -#import "AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.h" - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -static NSString *const kAppName = @"GACAppCheckStorageTestsApp"; -static NSString *const kGoogleAppID = @"1:100000000000:ios:aaaaaaaaaaaaaaaaaaaaaaaa"; - -@interface GACAppCheckStorageTests : XCTestCase -@property(nonatomic) NSString *tokenKey; -@property(nonatomic) GACAppCheckStorage *storage; -@end - -@implementation GACAppCheckStorageTests - -- (void)setUp { - [super setUp]; - - self.tokenKey = [self tokenKeyWithGoogleAppID:kGoogleAppID]; - self.storage = [[GACAppCheckStorage alloc] initWithTokenKey:self.tokenKey accessGroup:nil]; -} - -- (void)tearDown { - self.storage = nil; - [super tearDown]; -} - -- (void)testSetAndGetToken { - GACAppCheckToken *tokenToStore = [[GACAppCheckToken alloc] initWithToken:@"token" - expirationDate:[NSDate distantPast] - receivedAtDate:[NSDate date]]; - - FBLPromise *setPromise = [self.storage setToken:tokenToStore]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise.value, tokenToStore); - XCTAssertNil(setPromise.error); - - __auto_type getPromise = [self.storage getToken]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(getPromise.value.token, tokenToStore.token); - XCTAssertEqualObjects(getPromise.value.expirationDate, tokenToStore.expirationDate); - XCTAssertEqualObjects(getPromise.value.receivedAtDate, tokenToStore.receivedAtDate); - XCTAssertNil(getPromise.error); -} - -- (void)testRemoveToken { - FBLPromise *setPromise = [self.storage setToken:nil]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise.value, nil); - XCTAssertNil(setPromise.error); - - __auto_type getPromise = [self.storage getToken]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNil(getPromise.value); - XCTAssertNil(getPromise.error); -} - -- (void)testGetToken_KeychainError { - // 1. Set up storage mock. - id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]); - GACAppCheckStorage *storage = [[GACAppCheckStorage alloc] initWithTokenKey:self.tokenKey - keychainStorage:mockKeychainStorage - accessGroup:nil]; - // 2. Create and expect keychain error. - NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil]; - OCMExpect([mockKeychainStorage getObjectForKey:[OCMArg any] - objectClass:[OCMArg any] - accessGroup:[OCMArg any]]) - .andReturn([FBLPromise resolvedWith:gulsKeychainError]); - - // 3. Get token and verify results. - __auto_type getPromise = [storage getToken]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNotNil(getPromise.error); - XCTAssertEqualObjects(getPromise.error, - [GACAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]); - - // 4. Verify storage mock. - OCMVerifyAll(mockKeychainStorage); -} - -- (void)testSetToken_KeychainError { - // 1. Set up storage mock. - id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]); - GACAppCheckStorage *storage = [[GACAppCheckStorage alloc] initWithTokenKey:self.tokenKey - keychainStorage:mockKeychainStorage - accessGroup:nil]; - - // 2. Create and expect keychain error. - NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil]; - OCMExpect([mockKeychainStorage setObject:[OCMArg any] - forKey:[OCMArg any] - accessGroup:[OCMArg any]]) - .andReturn([FBLPromise resolvedWith:gulsKeychainError]); - - // 3. Set token and verify results. - GACAppCheckToken *tokenToStore = [[GACAppCheckToken alloc] initWithToken:@"token" - expirationDate:[NSDate distantPast] - receivedAtDate:[NSDate date]]; - __auto_type getPromise = [storage setToken:tokenToStore]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNotNil(getPromise.error); - XCTAssertEqualObjects(getPromise.error, - [GACAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]); - - // 4. Verify storage mock. - OCMVerifyAll(mockKeychainStorage); -} - -- (void)testRemoveToken_KeychainError { - // 1. Set up storage mock. - id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]); - GACAppCheckStorage *storage = [[GACAppCheckStorage alloc] initWithTokenKey:self.tokenKey - keychainStorage:mockKeychainStorage - accessGroup:nil]; - - // 2. Create and expect keychain error. - NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil]; - OCMExpect([mockKeychainStorage removeObjectForKey:[OCMArg any] accessGroup:[OCMArg any]]) - .andReturn([FBLPromise resolvedWith:gulsKeychainError]); - - // 3. Remove token and verify results. - __auto_type getPromise = [storage setToken:nil]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNotNil(getPromise.error); - XCTAssertEqualObjects(getPromise.error, - [GACAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]); - - // 4. Verify storage mock. - OCMVerifyAll(mockKeychainStorage); -} - -- (void)testSetTokenPerApp { - // 1. Set token with a storage. - GACAppCheckToken *tokenToStore = [[GACAppCheckToken alloc] initWithToken:@"token" - expirationDate:[NSDate distantPast] - receivedAtDate:[NSDate date]]; - - FBLPromise *setPromise = [self.storage setToken:tokenToStore]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertEqualObjects(setPromise.value, tokenToStore); - XCTAssertNil(setPromise.error); - - // 2. Try to read the token with another storage. - NSString *tokenKey = - [self tokenKeyWithGoogleAppID:@"1:200000000000:ios:aaaaaaaaaaaaaaaaaaaaaaaa"]; - GACAppCheckStorage *storage2 = [[GACAppCheckStorage alloc] initWithTokenKey:tokenKey - accessGroup:nil]; - __auto_type getPromise = [storage2 getToken]; - XCTAssert(FBLWaitForPromisesWithTimeout(0.5)); - XCTAssertNil(getPromise.value); - XCTAssertNil(getPromise.error); -} - -#pragma mark - Private Helpers - -- (NSString *)tokenKeyWithGoogleAppID:(NSString *)googleAppID { - return [NSString stringWithFormat:@"app_check_token.%@.%@", kAppName, googleAppID]; -} - -@end - -#endif // !TARGET_OS_MACCATALYST && !TARGET_OS_OSX - -#endif // !SWIFT_PACKAGE diff --git a/AppCheckCore/Tests/Unit/Core/GACAppCheckStoredTokenTests.m b/AppCheckCore/Tests/Unit/Core/GACAppCheckStoredTokenTests.m deleted file mode 100644 index bc2a5322e640..000000000000 --- a/AppCheckCore/Tests/Unit/Core/GACAppCheckStoredTokenTests.m +++ /dev/null @@ -1,71 +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 - -#import "AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken+GACAppCheckToken.h" -#import "AppCheckCore/Sources/Core/Storage/GACAppCheckStoredToken.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -#import - -@interface GACAppCheckStoredTokenTests : XCTestCase - -@end - -@implementation GACAppCheckStoredTokenTests - -- (void)testSecureCoding { - GACAppCheckStoredToken *tokenToArchive = [[GACAppCheckStoredToken alloc] init]; - tokenToArchive.token = @"some_token"; - tokenToArchive.expirationDate = [NSDate date]; - tokenToArchive.receivedAtDate = [tokenToArchive.expirationDate dateByAddingTimeInterval:-10]; - - NSError *error; - NSData *archivedToken = [GULSecureCoding archivedDataWithRootObject:tokenToArchive error:&error]; - XCTAssertNotNil(archivedToken); - XCTAssertNil(error); - - GACAppCheckStoredToken *unarchivedToken = - [GULSecureCoding unarchivedObjectOfClass:[GACAppCheckStoredToken class] - fromData:archivedToken - error:&error]; - XCTAssertNotNil(unarchivedToken); - XCTAssertNil(error); - XCTAssertEqualObjects(unarchivedToken.token, tokenToArchive.token); - XCTAssertEqualObjects(unarchivedToken.expirationDate, tokenToArchive.expirationDate); - XCTAssertEqualObjects(unarchivedToken.receivedAtDate, tokenToArchive.receivedAtDate); - XCTAssertEqual(unarchivedToken.storageVersion, tokenToArchive.storageVersion); -} - -- (void)testConvertingToAndFromGACAppCheckToken { - GACAppCheckToken *originalToken = [[GACAppCheckToken alloc] initWithToken:@"___" - expirationDate:[NSDate date] - receivedAtDate:[NSDate date]]; - - GACAppCheckStoredToken *storedToken = [[GACAppCheckStoredToken alloc] init]; - [storedToken updateWithToken:originalToken]; - XCTAssertEqualObjects(originalToken.token, storedToken.token); - XCTAssertEqualObjects(originalToken.expirationDate, storedToken.expirationDate); - XCTAssertEqualObjects(originalToken.receivedAtDate, storedToken.receivedAtDate); - - GACAppCheckToken *recoveredToken = [storedToken appCheckToken]; - XCTAssertEqualObjects(recoveredToken.token, storedToken.token); - XCTAssertEqualObjects(recoveredToken.expirationDate, storedToken.expirationDate); - XCTAssertEqualObjects(recoveredToken.receivedAtDate, storedToken.receivedAtDate); -} - -@end diff --git a/AppCheckCore/Tests/Unit/Core/GACAppCheckTests.m b/AppCheckCore/Tests/Unit/Core/GACAppCheckTests.m deleted file mode 100644 index ece1c89694e1..000000000000 --- a/AppCheckCore/Tests/Unit/Core/GACAppCheckTests.m +++ /dev/null @@ -1,695 +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 - -#import - -#import "FBLPromise+Testing.h" - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheck.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckProvider.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h" - -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/Core/Storage/GACAppCheckStorage.h" -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h" -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckTokenDelegate.h" - -#import "FirebaseCore/Extension/FirebaseCoreInternal.h" - -// The FAC token value returned when an error occurs. -static NSString *const kDummyToken = @"eyJlcnJvciI6IlVOS05PV05fRVJST1IifQ=="; - -static NSString *const kResourceName = @"projects/test_project_id/apps/test_app_id"; -static NSString *const kAppName = @"GACAppCheckTests"; -static NSString *const kAppGroupID = @"app_group_id"; - -@interface GACAppCheck (Tests) - -- (instancetype)initWithServiceName:(NSString *)instanceName - appCheckProvider:(id)appCheckProvider - storage:(id)storage - tokenRefresher:(id)tokenRefresher - settings:(id)settings - tokenDelegate:(nullable id)tokenDelegate; - -@end - -@interface GACAppCheckTests : XCTestCase - -@property(nonatomic) OCMockObject *mockStorage; -@property(nonatomic) OCMockObject *mockAppCheckProvider; -@property(nonatomic) OCMockObject *mockTokenRefresher; -@property(nonatomic) OCMockObject *mockSettings; -@property(nonatomic) OCMockObject *mockTokenDelegate; -@property(nonatomic) GACAppCheck *appCheck; - -@property(nonatomic, copy, nullable) GACAppCheckTokenRefreshBlock tokenRefreshHandler; - -@end - -@implementation GACAppCheckTests - -- (void)setUp { - [super setUp]; - - self.mockStorage = OCMStrictProtocolMock(@protocol(GACAppCheckStorageProtocol)); - self.mockAppCheckProvider = OCMStrictProtocolMock(@protocol(GACAppCheckProvider)); - self.mockTokenRefresher = OCMStrictProtocolMock(@protocol(GACAppCheckTokenRefresherProtocol)); - self.mockSettings = OCMStrictProtocolMock(@protocol(GACAppCheckSettingsProtocol)); - self.mockTokenDelegate = OCMStrictProtocolMock(@protocol(GACAppCheckTokenDelegate)); - - [self stubSetTokenRefreshHandler]; - - self.appCheck = [[GACAppCheck alloc] initWithServiceName:kAppName - appCheckProvider:self.mockAppCheckProvider - storage:self.mockStorage - tokenRefresher:self.mockTokenRefresher - settings:self.mockSettings - tokenDelegate:self.mockTokenDelegate]; -} - -- (void)tearDown { - self.appCheck = nil; - [self.mockAppCheckProvider stopMocking]; - self.mockAppCheckProvider = nil; - [self.mockStorage stopMocking]; - self.mockStorage = nil; - [self.mockTokenRefresher stopMocking]; - self.mockTokenRefresher = nil; - - [super tearDown]; -} - -#pragma mark - Public Init - -- (void)testAppCheckInit { - NSString *tokenKey = - [NSString stringWithFormat:@"app_check_token.%@.%@", kAppName, kResourceName]; - - // 1. Stub GACAppCheckTokenRefresher and validate usage. - id mockTokenRefresher = OCMClassMock([GACAppCheckTokenRefresher class]); - OCMExpect([mockTokenRefresher alloc]).andReturn(mockTokenRefresher); - - id refresherDateValidator = - [OCMArg checkWithBlock:^BOOL(GACAppCheckTokenRefreshResult *refreshResult) { - XCTAssertEqual(refreshResult.status, GACAppCheckTokenRefreshStatusNever); - XCTAssertEqual(refreshResult.tokenExpirationDate, nil); - XCTAssertEqual(refreshResult.tokenReceivedAtDate, nil); - return YES; - }]; - - id settingsValidator = [OCMArg checkWithBlock:^BOOL(id obj) { - XCTAssert([obj conformsToProtocol:@protocol(GACAppCheckSettingsProtocol)]); - return YES; - }]; - - OCMExpect([mockTokenRefresher initWithRefreshResult:refresherDateValidator - settings:settingsValidator]) - .andReturn(mockTokenRefresher); - OCMExpect([mockTokenRefresher setTokenRefreshHandler:[OCMArg any]]); - - // 2. Stub GACAppCheckStorage and validate usage. - id mockStorage = OCMStrictClassMock([GACAppCheckStorage class]); - OCMExpect([mockStorage alloc]).andReturn(mockStorage); - OCMExpect([mockStorage initWithTokenKey:tokenKey accessGroup:kAppGroupID]).andReturn(mockStorage); - - // 3. Stub attestation provider. - OCMockObject *mockProvider = - OCMStrictProtocolMock(@protocol(GACAppCheckProvider)); - - // 4. Stub GACAppCheckSettingsProtocol. - OCMockObject *mockSettings = - OCMStrictProtocolMock(@protocol(GACAppCheckSettingsProtocol)); - - // 5. Stub GACAppCheckTokenDelegate. - OCMockObject *mockTokenDelegate = - OCMStrictProtocolMock(@protocol(GACAppCheckTokenDelegate)); - - // 6. Call init. - GACAppCheck *appCheck = [[GACAppCheck alloc] initWithServiceName:kAppName - resourceName:kResourceName - appCheckProvider:mockProvider - settings:mockSettings - tokenDelegate:mockTokenDelegate - keychainAccessGroup:kAppGroupID]; - XCTAssert([appCheck isKindOfClass:[GACAppCheck class]]); - - // 7. Verify mocks. - OCMVerifyAll(mockTokenRefresher); - OCMVerifyAll(mockStorage); - OCMVerifyAll(mockProvider); - OCMVerifyAll(mockSettings); - OCMVerifyAll(mockTokenDelegate); - - // 8. Stop mocking real class mocks. - [mockTokenRefresher stopMocking]; - mockTokenRefresher = nil; - [mockStorage stopMocking]; - mockStorage = nil; -} - -#pragma mark - Public Get Token - -- (void)testGetToken_WhenCachedTokenIsValid_Success { - [self assertGetToken_WhenCachedTokenIsValid_Success]; -} - -#pragma mark - GACAppCheckInterop Get Token - -- (void)testInteropGetToken_WhenNoCache_Success { - // 1. Create expected token and configure expectations. - GACAppCheckToken *expectedToken = [self validToken]; - - XCTestExpectation *expectation = - [self configuredExpectations_GetTokenWhenNoCache_withExpectedToken:expectedToken]; - - // 2. Request token and verify result. - [self.appCheck - getTokenForcingRefresh:NO - completion:^(id _Nullable token, NSError *error) { - [expectation fulfill]; - XCTAssertNotNil(token); - XCTAssertEqualObjects(token.token, expectedToken.token); - XCTAssertNil(error); - }]; - - // 3. Wait for expectations and validate mocks. - [self waitForExpectations:@[ expectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -- (void)testInteropGetToken_WhenCachedTokenIsValid_Success { - [self assertInteropGetToken_WhenCachedTokenIsValid_Success]; -} - -- (void)testInteropGetTokenForcingRefresh_WhenCachedTokenIsValid_Success { - // 1. Create expected token and configure expectations. - GACAppCheckToken *expectedToken = [self validToken]; - XCTestExpectation *expectation = - [self configuredExpectations_GetTokenForcingRefreshWhenCacheIsValid_withExpectedToken: - expectedToken]; - - // 2. Request token and verify result. - [self.appCheck - getTokenForcingRefresh:YES - completion:^(id _Nullable token, NSError *error) { - [expectation fulfill]; - XCTAssertNotNil(token); - XCTAssertEqualObjects(token.token, expectedToken.token); - XCTAssertNil(error); - }]; - - // 3. Wait for expectations and validate mocks. - [self waitForExpectations:@[ expectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -- (void)testInteropGetToken_WhenCachedTokenExpired_Success { - // 1. Create expected token and configure expectations. - GACAppCheckToken *expectedToken = [self validToken]; - - XCTestExpectation *expectation = - [self configuredExpectations_GetTokenWhenCachedTokenExpired_withExpectedToken:expectedToken]; - - // 2. Request token and verify result. - [self.appCheck - getTokenForcingRefresh:NO - completion:^(id _Nullable token, NSError *error) { - [expectation fulfill]; - XCTAssertNotNil(token); - XCTAssertEqualObjects(token.token, expectedToken.token); - XCTAssertNil(error); - }]; - - // 3. Wait for expectations and validate mocks. - [self waitForExpectations:@[ expectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -- (void)testInteropGetToken_AppCheckProviderError { - // 1. Create expected tokens and errors and configure expectations. - GACAppCheckToken *cachedToken = [self soonExpiringToken]; - NSError *providerError = [NSError errorWithDomain:@"GACAppCheckTests" code:-1 userInfo:nil]; - - XCTestExpectation *expectation = - [self configuredExpectations_GetTokenWhenError_withError:providerError andToken:cachedToken]; - - // 2. Request token and verify result. - [self.appCheck - getTokenForcingRefresh:NO - completion:^(id _Nullable token, NSError *error) { - [expectation fulfill]; - XCTAssertNil(token); - XCTAssertEqualObjects(error, providerError); - // Interop API does not wrap errors in public domain. - XCTAssertNotEqualObjects(error.domain, GACAppCheckErrorDomain); - }]; - - // 3. Wait for expectations and validate mocks. - [self waitForExpectations:@[ expectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -#pragma mark - Token refresher - -- (void)testTokenRefreshTriggeredAndRefreshSuccess { - // 1. Expect token to be requested from storage. - OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]); - - // 2. Expect token requested from app check provider. - NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:10000]; - GACAppCheckToken *tokenToReturn = [[GACAppCheckToken alloc] initWithToken:@"valid" - expirationDate:expirationDate]; - id completionArg = [OCMArg invokeBlockWithArgs:tokenToReturn, [NSNull null], nil]; - OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]); - - // 3. Expect new token to be stored. - OCMExpect([self.mockStorage setToken:tokenToReturn]) - .andReturn([FBLPromise resolvedWith:tokenToReturn]); - OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]); - - // 4. Expect token update notification to be sent. - OCMExpect([self.mockTokenDelegate tokenDidUpdate:tokenToReturn serviceName:kAppName]); - - // 5. Trigger refresh and expect the result. - if (self.tokenRefreshHandler == nil) { - XCTFail(@"`tokenRefreshHandler` must be not `nil`."); - return; - } - - XCTestExpectation *completionExpectation = [self expectationWithDescription:@"completion"]; - self.tokenRefreshHandler(^(GACAppCheckTokenRefreshResult *refreshResult) { - [completionExpectation fulfill]; - XCTAssertEqualObjects(refreshResult.tokenExpirationDate, expirationDate); - XCTAssertEqual(refreshResult.status, GACAppCheckTokenRefreshStatusSuccess); - }); - - [self waitForExpectations:@[ completionExpectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -- (void)testTokenRefreshTriggeredAndRefreshError { - // 1. Expect token to be requested from storage. - OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]); - - // 2. Expect token requested from app check provider. - NSError *providerError = [self internalError]; - id completionArg = [OCMArg invokeBlockWithArgs:[NSNull null], providerError, nil]; - OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]); - - // 3. Don't expect token requested from app check provider. - OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]); - - // 4. Don't expect token update notification to be sent. - OCMReject([self.mockTokenDelegate tokenDidUpdate:OCMOCK_ANY serviceName:OCMOCK_ANY]); - - // 5. Trigger refresh and expect the result. - if (self.tokenRefreshHandler == nil) { - XCTFail(@"`tokenRefreshHandler` must be not `nil`."); - return; - } - - XCTestExpectation *completionExpectation = [self expectationWithDescription:@"completion"]; - self.tokenRefreshHandler(^(GACAppCheckTokenRefreshResult *refreshResult) { - [completionExpectation fulfill]; - XCTAssertEqual(refreshResult.status, GACAppCheckTokenRefreshStatusFailure); - XCTAssertNil(refreshResult.tokenExpirationDate); - XCTAssertNil(refreshResult.tokenReceivedAtDate); - }); - - [self waitForExpectations:@[ completionExpectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -- (void)testLimitedUseTokenWithSuccess { - // 1. Don't expect token to be requested from storage. - OCMReject([self.mockStorage getToken]); - - // 2. Expect token requested from app check provider. - GACAppCheckToken *expectedToken = [self validToken]; - id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil]; - OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]); - - // 3. Don't expect token requested from storage. - OCMReject([self.mockStorage setToken:expectedToken]); - - // 4. Don't expect token update notification to be sent. - OCMReject([self.mockTokenDelegate tokenDidUpdate:OCMOCK_ANY serviceName:OCMOCK_ANY]); - - // 5. Expect token request to be completed. - XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"]; - - [self.appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, - NSError *_Nullable error) { - [getTokenExpectation fulfill]; - XCTAssertNotNil(token); - XCTAssertEqualObjects(token.token, expectedToken.token); - XCTAssertNil(error); - }]; - [self waitForExpectations:@[ getTokenExpectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -- (void)testLimitedUseToken_WhenTokenGenerationErrors { - // 1. Don't expect token to be requested from storage. - OCMReject([self.mockStorage getToken]); - - // 2. Expect error when requesting token from app check provider. - NSError *providerError = [GACAppCheckErrorUtil keychainErrorWithError:[self internalError]]; - id completionArg = [OCMArg invokeBlockWithArgs:[NSNull null], providerError, nil]; - OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]); - - // 3. Don't expect token requested from app check provider. - OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]); - - // 4. Don't expect token update notification to be sent. - OCMReject([self.mockTokenDelegate tokenDidUpdate:OCMOCK_ANY serviceName:OCMOCK_ANY]); - - // 5. Expect token request to be completed. - XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"]; - - [self.appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, - NSError *_Nullable error) { - [getTokenExpectation fulfill]; - XCTAssertNotNil(error); - XCTAssertNil(token.token); - XCTAssertEqualObjects(error, providerError); - XCTAssertEqualObjects(error.domain, GACAppCheckErrorDomain); - }]; - - [self waitForExpectations:@[ getTokenExpectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -#pragma mark - Merging multiple get token requests - -- (void)testInteropGetToken_WhenCalledSeveralTimesSuccess_ThenThereIsOnlyOneOperation { - // 1. Expect a token to be requested and stored. - NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise = - [self expectTokenRequestFromAppCheckProvider]; - GACAppCheckToken *expectedToken = expectedTokenAndPromise.firstObject; - FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject; - OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]); - - // 2. Expect token update notification to be sent. - OCMExpect([self.mockTokenDelegate tokenDidUpdate:expectedToken serviceName:kAppName]); - - // 3. Request token several times. - NSInteger getTokenCallsCount = 10; - NSMutableArray *getTokenCompletionExpectations = - [NSMutableArray arrayWithCapacity:getTokenCallsCount]; - - for (NSInteger i = 0; i < getTokenCallsCount; i++) { - // 3.1. Expect a completion to be called for each method call. - XCTestExpectation *getTokenExpectation = - [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]]; - [getTokenCompletionExpectations addObject:getTokenExpectation]; - - // 3.2. Request token and verify result. - [self.appCheck - getTokenForcingRefresh:NO - completion:^(id _Nullable token, NSError *error) { - [getTokenExpectation fulfill]; - XCTAssertNotNil(token); - XCTAssertEqualObjects(token.token, expectedToken.token); - XCTAssertNil(error); - }]; - } - - // 3.3. Fulfill the pending promise to finish the get token operation. - [storeTokenPromise fulfill:expectedToken]; - - // 4. Wait for expectations and validate mocks. - [self waitForExpectations:getTokenCompletionExpectations timeout:0.5]; - [self verifyAllMocks]; - - // 5. Check a get token call after. - [self assertInteropGetToken_WhenCachedTokenIsValid_Success]; -} - -- (void)testInteropGetToken_WhenCalledSeveralTimesError_ThenThereIsOnlyOneOperation { - // 1. Expect a token to be requested and stored. - NSArray * /*[expectedToken, storeTokenPromise]*/ expectedTokenAndPromise = - [self expectTokenRequestFromAppCheckProvider]; - FBLPromise *storeTokenPromise = expectedTokenAndPromise.lastObject; - - // 1.1. Create an expected error to be reject the store token promise with later. - NSError *storageError = [NSError errorWithDomain:self.name code:0 userInfo:nil]; - - // 2. Don't expect token update notification to be sent. - OCMReject([self.mockTokenDelegate tokenDidUpdate:OCMOCK_ANY serviceName:OCMOCK_ANY]); - - // 3. Request token several times. - NSInteger getTokenCallsCount = 10; - NSMutableArray *getTokenCompletionExpectations = - [NSMutableArray arrayWithCapacity:getTokenCallsCount]; - - for (NSInteger i = 0; i < getTokenCallsCount; i++) { - // 3.1. Expect a completion to be called for each method call. - XCTestExpectation *getTokenExpectation = - [self expectationWithDescription:[NSString stringWithFormat:@"getToken%@", @(i)]]; - [getTokenCompletionExpectations addObject:getTokenExpectation]; - - // 3.2. Request token and verify result. - [self.appCheck - getTokenForcingRefresh:NO - completion:^(id _Nullable token, NSError *error) { - [getTokenExpectation fulfill]; - XCTAssertNil(token); - XCTAssertEqualObjects(error, storageError); - }]; - } - - // 3.3. Reject the pending promise to finish the get token operation. - [storeTokenPromise reject:storageError]; - - // 4. Wait for expectations and validate mocks. - [self waitForExpectations:getTokenCompletionExpectations timeout:0.5]; - [self verifyAllMocks]; - - // 5. Check a get token call after. - [self assertInteropGetToken_WhenCachedTokenIsValid_Success]; -} - -#pragma mark - Helpers - -- (NSError *)internalError { - return [NSError errorWithDomain:@"com.internal.error" code:-1 userInfo:nil]; -} - -- (GACAppCheckToken *)validToken { - return [[GACAppCheckToken alloc] initWithToken:[NSUUID UUID].UUIDString - expirationDate:[NSDate distantFuture]]; -} - -- (GACAppCheckToken *)soonExpiringToken { - NSDate *soonExpiringTokenDate = [NSDate dateWithTimeIntervalSinceNow:4.5 * 60]; - return [[GACAppCheckToken alloc] initWithToken:@"valid" expirationDate:soonExpiringTokenDate]; -} - -- (void)stubSetTokenRefreshHandler { - id arg = [OCMArg checkWithBlock:^BOOL(id handler) { - self.tokenRefreshHandler = handler; - return YES; - }]; - OCMExpect([self.mockTokenRefresher setTokenRefreshHandler:arg]); -} - -- (void)assertGetToken_WhenCachedTokenIsValid_Success { - // 1. Create expected token and configure expectations. - GACAppCheckToken *cachedToken = [self validToken]; - - XCTestExpectation *expectation = - [self configuredExpectation_GetTokenWhenCacheTokenIsValid_withExpectedToken:cachedToken]; - - // 2. Request token and verify result. - [self.appCheck getTokenForcingRefresh:NO - completion:^(id _Nullable token, - NSError *_Nullable error) { - [expectation fulfill]; - XCTAssertNotNil(token); - XCTAssertEqualObjects(token.token, cachedToken.token); - XCTAssertNil(error); - }]; - - // 3. Wait for expectations and validate mocks. - [self waitForExpectations:@[ expectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -- (void)assertInteropGetToken_WhenCachedTokenIsValid_Success { - // 1. Create expected token and configure expectations. - GACAppCheckToken *cachedToken = [self validToken]; - - XCTestExpectation *expectation = - [self configuredExpectation_GetTokenWhenCacheTokenIsValid_withExpectedToken:cachedToken]; - - // 2. Request token and verify result. - [self.appCheck - getTokenForcingRefresh:NO - completion:^(id _Nullable token, NSError *error) { - [expectation fulfill]; - XCTAssertNotNil(token); - XCTAssertEqualObjects(token.token, cachedToken.token); - XCTAssertNil(error); - }]; - - // 3. Wait for expectations and validate mocks. - [self waitForExpectations:@[ expectation ] timeout:0.5]; - [self verifyAllMocks]; -} - -- (XCTestExpectation *)configuredExpectations_GetTokenWhenNoCache_withExpectedToken: - (GACAppCheckToken *)expectedToken { - // 1. Expect token to be requested from storage. - OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]); - - // 2. Expect token requested from app check provider. - id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil]; - OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]); - - // 3. Expect new token to be stored. - OCMExpect([self.mockStorage setToken:expectedToken]) - .andReturn([FBLPromise resolvedWith:expectedToken]); - OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]); - - // 4. Expect token update notification to be sent. - OCMExpect([self.mockTokenDelegate tokenDidUpdate:expectedToken serviceName:kAppName]); - - // 5. Expect token request to be completed. - XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"]; - - return getTokenExpectation; -} - -- (XCTestExpectation *)configuredExpectation_GetTokenWhenCacheTokenIsValid_withExpectedToken: - (GACAppCheckToken *)expectedToken { - // 1. Expect token to be requested from storage. - OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:expectedToken]); - - // 2. Don't expect token requested from app check provider. - OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]); - - // 3. Don't expect token update notification to be sent. - OCMReject([self.mockTokenDelegate tokenDidUpdate:OCMOCK_ANY serviceName:OCMOCK_ANY]); - - // 4. Expect token request to be completed. - return [self expectationWithDescription:@"getToken"]; -} - -- (XCTestExpectation *) - configuredExpectations_GetTokenForcingRefreshWhenCacheIsValid_withExpectedToken: - (GACAppCheckToken *)expectedToken { - // 1. Don't expect token to be requested from storage. - OCMReject([self.mockStorage getToken]); - - // 2. Expect token requested from app check provider. - id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil]; - OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]); - - // 3. Expect new token to be stored. - OCMExpect([self.mockStorage setToken:expectedToken]) - .andReturn([FBLPromise resolvedWith:expectedToken]); - OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]); - - // 4. Expect token update notification to be sent. - OCMExpect([self.mockTokenDelegate tokenDidUpdate:expectedToken serviceName:kAppName]); - - // 5. Expect token request to be completed. - XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"]; - - return getTokenExpectation; -} - -- (XCTestExpectation *)configuredExpectations_GetTokenWhenCachedTokenExpired_withExpectedToken: - (GACAppCheckToken *)expectedToken { - // 1. Expect token to be requested from storage. - GACAppCheckToken *cachedToken = [[GACAppCheckToken alloc] initWithToken:@"expired" - expirationDate:[NSDate date]]; - OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:cachedToken]); - - // 2. Expect token requested from app check provider. - id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil]; - OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]); - - // 3. Expect new token to be stored. - OCMExpect([self.mockStorage setToken:expectedToken]) - .andReturn([FBLPromise resolvedWith:expectedToken]); - OCMExpect([self.mockTokenRefresher updateWithRefreshResult:[OCMArg any]]); - - // 4. Expect token update notification to be sent. - OCMExpect([self.mockTokenDelegate tokenDidUpdate:expectedToken serviceName:kAppName]); - - // 5. Expect token request to be completed. - XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"]; - - return getTokenExpectation; -} - -- (XCTestExpectation *) - configuredExpectations_GetTokenWhenError_withError:(NSError *_Nonnull)error - andToken:(GACAppCheckToken *_Nullable)token { - // 1. Expect token to be requested from storage. - OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:token]); - - // 2. Expect token requested from app check provider. - id completionArg = [OCMArg invokeBlockWithArgs:[NSNull null], error, nil]; - OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]); - - // 3. Don't expect token requested from app check provider. - OCMReject([self.mockAppCheckProvider getTokenWithCompletion:[OCMArg any]]); - - // 4. Expect token update notification to be sent. - OCMReject([self.mockTokenDelegate tokenDidUpdate:OCMOCK_ANY serviceName:OCMOCK_ANY]); - - // 5. Expect token request to be completed. - XCTestExpectation *getTokenExpectation = [self expectationWithDescription:@"getToken"]; - - return getTokenExpectation; -} - -- (NSArray *)expectTokenRequestFromAppCheckProvider { - // 1. Expect token to be requested from storage. - OCMExpect([self.mockStorage getToken]).andReturn([FBLPromise resolvedWith:nil]); - - // 2. Expect token requested from app check provider. - GACAppCheckToken *expectedToken = [self validToken]; - id completionArg = [OCMArg invokeBlockWithArgs:expectedToken, [NSNull null], nil]; - OCMExpect([self.mockAppCheckProvider getTokenWithCompletion:completionArg]); - - // 3. Expect new token to be stored. - // 3.1. Create a pending promise to resolve later. - FBLPromise *storeTokenPromise = [FBLPromise pendingPromise]; - // 3.2. Stub storage set token method. - OCMExpect([self.mockStorage setToken:expectedToken]).andReturn(storeTokenPromise); - - return @[ expectedToken, storeTokenPromise ]; -} - -- (void)verifyAllMocks { - OCMVerifyAll(self.mockAppCheckProvider); - OCMVerifyAll(self.mockStorage); - OCMVerifyAll(self.mockSettings); - OCMVerifyAll(self.mockTokenDelegate); - OCMVerifyAll(self.mockTokenRefresher); -} - -@end diff --git a/AppCheckCore/Tests/Unit/Core/GACAppCheckTimerTests.m b/AppCheckCore/Tests/Unit/Core/GACAppCheckTimerTests.m deleted file mode 100644 index 8673ca26abe6..000000000000 --- a/AppCheckCore/Tests/Unit/Core/GACAppCheckTimerTests.m +++ /dev/null @@ -1,74 +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 - -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.h" - -@interface GACAppCheckTimerTests : XCTestCase - -@end - -@implementation GACAppCheckTimerTests - -- (void)testTimerProvider { - dispatch_queue_t queue = - dispatch_queue_create("GACAppCheckTimerTests.testInit", DISPATCH_QUEUE_SERIAL); - NSTimeInterval fireTimerIn = 1; - NSDate *startTime = [NSDate date]; - NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:fireTimerIn]; - - GACTimerProvider timerProvider = [GACAppCheckTimer timerProvider]; - - XCTestExpectation *timerExpectation = [self expectationWithDescription:@"timer"]; - GACAppCheckTimer *timer = timerProvider(fireDate, queue, ^{ - NSTimeInterval actuallyFiredIn = [[NSDate date] timeIntervalSinceDate:startTime]; - // Check that fired at proper time (allowing some timer drift). - XCTAssertLessThan(ABS(actuallyFiredIn - fireTimerIn), 0.5); - - [timerExpectation fulfill]; - }); - - XCTAssertNotNil(timer); - - [self waitForExpectations:@[ timerExpectation ] timeout:fireTimerIn + 1]; -} - -- (void)testInit { - dispatch_queue_t queue = - dispatch_queue_create("GACAppCheckTimerTests.testInit", DISPATCH_QUEUE_SERIAL); - NSTimeInterval fireTimerIn = 2; - NSDate *startTime = [NSDate date]; - NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:fireTimerIn]; - - XCTestExpectation *timerExpectation = [self expectationWithDescription:@"timer"]; - GACAppCheckTimer *timer = [[GACAppCheckTimer alloc] - initWithFireDate:fireDate - dispatchQueue:queue - block:^{ - NSTimeInterval actuallyFiredIn = [[NSDate date] timeIntervalSinceDate:startTime]; - // Check that fired at proper time (allowing some timer drift). - XCTAssertLessThan(ABS(actuallyFiredIn - fireTimerIn), 0.5); - - [timerExpectation fulfill]; - }]; - - XCTAssertNotNil(timer); - - [self waitForExpectations:@[ timerExpectation ] timeout:fireTimerIn + 1]; -} - -@end diff --git a/AppCheckCore/Tests/Unit/Core/GACAppCheckTokenRefresherTests.m b/AppCheckCore/Tests/Unit/Core/GACAppCheckTokenRefresherTests.m deleted file mode 100644 index 7a317807746a..000000000000 --- a/AppCheckCore/Tests/Unit/Core/GACAppCheckTokenRefresherTests.m +++ /dev/null @@ -1,503 +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 - -#import - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckSettings.h" - -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefreshResult.h" -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTokenRefresher.h" -#import "AppCheckCore/Tests/Unit/Utils/GACFakeTimer.h" -#import "AppCheckCore/Tests/Utils/Date/GACDateTestUtils.h" - -@interface GACAppCheckTokenRefresherTests : XCTestCase - -@property(nonatomic) GACFakeTimer *fakeTimer; - -@property(nonatomic) OCMockObject *mockSettings; - -@property(nonatomic) GACAppCheckTokenRefreshResult *initialTokenRefreshResult; - -@end - -@implementation GACAppCheckTokenRefresherTests - -- (void)setUp { - self.mockSettings = OCMProtocolMock(@protocol(GACAppCheckSettingsProtocol)); - self.fakeTimer = [[GACFakeTimer alloc] init]; - - NSDate *receivedAtDate = [NSDate date]; - self.initialTokenRefreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:[receivedAtDate dateByAddingTimeInterval:1000] - receivedAtDate:receivedAtDate]; -} - -- (void)tearDown { - self.fakeTimer = nil; - [self.mockSettings stopMocking]; - self.mockSettings = nil; -} - -#pragma mark - Auto refresh is allowed - -- (void)testInitialRefreshWhenAutoRefreshAllowed { - __auto_type weakSelf = self; - - self.initialTokenRefreshResult = [[GACAppCheckTokenRefreshResult alloc] initWithStatusNever]; - GACAppCheckTokenRefresher *refresher = [self createRefresher]; - - // 1. Expect checking if auto-refresh allowed before scheduling the initial refresh. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 2. Don't expect the timer to be scheduled for the first refresh as the refresh should be - // triggered straight away. - XCTestExpectation *initialTimerCreatedExpectation = - [self expectationWithDescription:@"initial refresh timer created"]; - initialTimerCreatedExpectation.inverted = YES; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - weakSelf.fakeTimer.createHandler = nil; - [initialTimerCreatedExpectation fulfill]; - }; - - // 3. Expect checking if auto-refresh allowed before triggering the initial refresh. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 4. Expect initial refresh handler to be called. - __block GACAppCheckTokenRefreshCompletion initialRefreshCompletion; - XCTestExpectation *initialRefreshExpectation = - [self expectationWithDescription:@"initial refresh"]; - refresher.tokenRefreshHandler = ^(GACAppCheckTokenRefreshCompletion _Nonnull completion) { - // Save completion to be called later. - initialRefreshCompletion = completion; - - [initialRefreshExpectation fulfill]; - }; - - NSDate *initialTokenExpirationDate = [NSDate dateWithTimeIntervalSinceNow:60 * 60]; - NSDate *initialTokenReceivedDate = [NSDate date]; - __auto_type initialRefreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:initialTokenExpirationDate - receivedAtDate:initialTokenReceivedDate]; - - [self waitForExpectations:@[ initialTimerCreatedExpectation, initialRefreshExpectation ] - timeout:1]; - - // 5. Expect checking if auto-refresh allowed before scheduling next refresh. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 6. Expect a next refresh timer to be scheduled on initial refresh completion. - NSDate *expectedRefreshDate = - [self expectedRefreshDateWithReceivedDate:initialTokenReceivedDate - expirationDate:initialTokenExpirationDate]; - XCTestExpectation *nextTimerCreateExpectation = - [self expectationWithDescription:@"next refresh create timer"]; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - weakSelf.fakeTimer.createHandler = nil; - XCTAssertEqualObjects(fireDate, expectedRefreshDate); - [nextTimerCreateExpectation fulfill]; - }; - - // 7. Call initial refresh completion and wait for next refresh timer to be scheduled. - initialRefreshCompletion(initialRefreshResult); - [self waitForExpectations:@[ nextTimerCreateExpectation ] timeout:0.5]; - - // 8. Expect checking if auto-refresh allowed before triggering the next refresh. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 9. Expect refresh handler to be called for the next refresh. - __auto_type nextRefreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:[expectedRefreshDate dateByAddingTimeInterval:60 * 60] - receivedAtDate:expectedRefreshDate]; - XCTestExpectation *nextRefreshExpectation = [self expectationWithDescription:@"next refresh"]; - refresher.tokenRefreshHandler = ^(GACAppCheckTokenRefreshCompletion _Nonnull completion) { - [nextRefreshExpectation fulfill]; - - // Call completion. - completion(nextRefreshResult); - }; - - // 10. Fire the timer. - [self fireTimer]; - - // 11. Wait for the next refresh handler to be called. - [self waitForExpectations:@[ nextRefreshExpectation ] timeout:1]; - - OCMVerifyAll(self.mockSettings); -} - -- (void)testNoTimeScheduledUntilHandlerSet { - // 1. Don't expect timer to be scheduled. - XCTestExpectation *timerCreateExpectation1 = [self expectationWithDescription:@"create timer 1"]; - timerCreateExpectation1.inverted = YES; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - [timerCreateExpectation1 fulfill]; - }; - - // 2. Create a publisher. - GACAppCheckTokenRefresher *refresher = [self createRefresher]; - - XCTAssertNotNil(refresher); - - [self waitForExpectations:@[ timerCreateExpectation1 ] timeout:0.5]; - - // 3. Expect timer to be created after the handler has been set. - // 3.1. Expect checking if auto-refresh allowed one more time when timer fires. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 3.2. Expect timer to fire. - XCTestExpectation *timerCreateExpectation2 = [self expectationWithDescription:@"create timer 2"]; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - [timerCreateExpectation2 fulfill]; - }; - - // 3.3. Set handler. - refresher.tokenRefreshHandler = ^(GACAppCheckTokenRefreshCompletion _Nonnull completion) { - }; - - [self waitForExpectations:@[ timerCreateExpectation2 ] timeout:0.5]; - - OCMVerifyAll(self.mockSettings); -} - -- (void)testNextRefreshOnRefreshSuccess { - GACAppCheckTokenRefresher *refresher = [self createRefresher]; - - NSDate *refreshedTokenExpirationDate = - [self.initialTokenRefreshResult.tokenExpirationDate dateByAddingTimeInterval:60 * 60]; - __auto_type refreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:refreshedTokenExpirationDate - receivedAtDate:self.initialTokenRefreshResult.tokenExpirationDate]; - - // 1. Expect checking if auto-refresh allowed before scheduling initial refresh. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 2. Expect checking if auto-refresh allowed before calling the refresh handler. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 3. Expect refresh handler. - XCTestExpectation *initialRefreshExpectation = - [self expectationWithDescription:@"initial refresh"]; - refresher.tokenRefreshHandler = ^(GACAppCheckTokenRefreshCompletion _Nonnull completion) { - [initialRefreshExpectation fulfill]; - - // Call completion in a while. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - completion(refreshResult); - }); - }; - - // 4. Expect for new timer to be created. - NSDate *expectedFireDate = - [self expectedRefreshDateWithReceivedDate:refreshResult.tokenReceivedAtDate - expirationDate:refreshResult.tokenExpirationDate]; - XCTestExpectation *createTimerExpectation = [self expectationWithDescription:@"create timer"]; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - [createTimerExpectation fulfill]; - XCTAssertEqualObjects(fireDate, expectedFireDate); - }; - - // 5. Expect checking if auto-refresh allowed before refreshing. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 6. Fire initial timer and wait for expectations. - [self fireTimer]; - - [self waitForExpectations:@[ initialRefreshExpectation, createTimerExpectation ] - timeout:1 - enforceOrder:YES]; - - OCMVerifyAll(self.mockSettings); -} - -- (void)testBackoff { - GACAppCheckTokenRefresher *refresher = [self createRefresher]; - - // Initial backoff interval. - NSTimeInterval expectedBackoffTime = 0; - NSTimeInterval maximumBackoffTime = 16 * 60; // 16 min. - - // 1. Expect checking if auto-refresh allowed before scheduling initial refresh. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - for (NSInteger i = 0; i < 10; i++) { - // 2. Expect checking if auto-refresh allowed before calling the refresh handler. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 3. Expect refresh handler. - XCTestExpectation *initialRefreshExpectation = - [self expectationWithDescription:@"initial refresh"]; - refresher.tokenRefreshHandler = ^(GACAppCheckTokenRefreshCompletion _Nonnull completion) { - [initialRefreshExpectation fulfill]; - - // Call completion in a while. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - __auto_type refreshFailure = - [[GACAppCheckTokenRefreshResult alloc] initWithStatusFailure]; - completion(refreshFailure); - }); - }; - - // 4. Expect for new timer to be created. - // No backoff initially, 1st backoff 30sec, double backoff on each next attempt until 16min. - expectedBackoffTime = expectedBackoffTime == 0 ? 30 : expectedBackoffTime * 2; - expectedBackoffTime = MIN(expectedBackoffTime, maximumBackoffTime); - NSDate *expectedFireDate = [[NSDate date] dateByAddingTimeInterval:expectedBackoffTime]; - - XCTestExpectation *createTimerExpectation = [self expectationWithDescription:@"create timer"]; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - [createTimerExpectation fulfill]; - - // Check expected and actual fire date are not too different (account for the random part - // and request attempt delay). - XCTAssertLessThan(ABS([expectedFireDate timeIntervalSinceDate:fireDate]), 2); - }; - - // 5. Expect checking if auto-refresh allowed before refreshing. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 6. Fire initial timer and wait for expectations. - [self fireTimer]; - - [self waitForExpectations:@[ initialRefreshExpectation, createTimerExpectation ] - timeout:1 - enforceOrder:YES]; - } - - OCMVerifyAll(self.mockSettings); -} - -#pragma mark - Auto refresh is not allowed - -- (void)testNoInitialRefreshWhenAutoRefreshIsNotAllowed { - GACAppCheckTokenRefresher *refresher = [self createRefresher]; - - // 1. Expect checking if auto-refresh allowed before scheduling initial refresh. - [[[self.mockSettings expect] andReturnValue:@(NO)] isTokenAutoRefreshEnabled]; - - // 2. Don't expect timer to be scheduled. - XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"]; - timerCreateExpectation.inverted = YES; - - __auto_type weakSelf = self; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - weakSelf.fakeTimer.createHandler = nil; - [timerCreateExpectation fulfill]; - }; - - // 3. Don't expect refresh handler to be called. - __auto_type refreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:[NSDate dateWithTimeIntervalSinceNow:60 * 60] - receivedAtDate:[NSDate date]]; - XCTestExpectation *refreshExpectation = [self expectationWithDescription:@"refresh"]; - refreshExpectation.inverted = YES; - - refresher.tokenRefreshHandler = ^(GACAppCheckTokenRefreshCompletion _Nonnull completion) { - [refreshExpectation fulfill]; - - // Call completion. - completion(refreshResult); - }; - - // 4. Check if the handler is not fired before the timer. - [self waitForExpectations:@[ timerCreateExpectation, refreshExpectation ] timeout:1]; - - OCMVerifyAll(self.mockSettings); -} - -- (void)testNoRefreshWhenAutoRefreshWasDisabledAfterInit { - GACAppCheckTokenRefresher *refresher = [self createRefresher]; - - // 1. Expect checking if auto-refresh allowed before scheduling initial refresh. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 2. Expect timer to be scheduled. - NSDate *expectedTimerFireDate = - [self expectedRefreshDateWithReceivedDate:self.initialTokenRefreshResult.tokenReceivedAtDate - expirationDate:self.initialTokenRefreshResult.tokenExpirationDate]; - XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"]; - - __auto_type weakSelf = self; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - weakSelf.fakeTimer.createHandler = nil; - XCTAssertEqualObjects(fireDate, expectedTimerFireDate); - [timerCreateExpectation fulfill]; - }; - - // 3. Expect refresh handler to be called. - __auto_type refreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:[expectedTimerFireDate - dateByAddingTimeInterval:60 * 60] - receivedAtDate:expectedTimerFireDate]; - XCTestExpectation *noRefreshExpectation = [self expectationWithDescription:@"initial refresh"]; - noRefreshExpectation.inverted = YES; - refresher.tokenRefreshHandler = ^(GACAppCheckTokenRefreshCompletion _Nonnull completion) { - [noRefreshExpectation fulfill]; - - // Call completion. - completion(refreshResult); - }; - - // 4. Check if the handler is not fired before the timer. - [self waitForExpectations:@[ timerCreateExpectation ] timeout:1]; - - // 5. Expect checking if auto-refresh allowed before refreshing. - [[[self.mockSettings expect] andReturnValue:@(NO)] isTokenAutoRefreshEnabled]; - - // 6. Fire the timer and wait for completion. - [self fireTimer]; - - [self waitForExpectations:@[ noRefreshExpectation ] timeout:1]; - - OCMVerifyAll(self.mockSettings); -} - -#pragma mark - Update token expiration - -- (void)testUpdateWithRefreshResultWhenAutoRefreshIsAllowed { - GACAppCheckTokenRefresher *refresher = [self createRefresher]; - - NSDate *newExpirationDate = - [self.initialTokenRefreshResult.tokenExpirationDate dateByAddingTimeInterval:10 * 60]; - __auto_type newRefreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:newExpirationDate - receivedAtDate:self.initialTokenRefreshResult.tokenExpirationDate]; - - // 1. Expect checking if auto-refresh allowed before scheduling refresh. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 2. Expect timer to be scheduled. - NSDate *expectedTimerFireDate = - [self expectedRefreshDateWithReceivedDate:newRefreshResult.tokenReceivedAtDate - expirationDate:newRefreshResult.tokenExpirationDate]; - XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"]; - - __auto_type weakSelf = self; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - weakSelf.fakeTimer.createHandler = nil; - XCTAssertEqualObjects(fireDate, expectedTimerFireDate); - [timerCreateExpectation fulfill]; - }; - - // 3. Update token expiration date. - [refresher updateWithRefreshResult:newRefreshResult]; - - // 4. Wait for timer to be created. - [self waitForExpectations:@[ timerCreateExpectation ] timeout:1]; - - OCMVerifyAll(self.mockSettings); -} - -- (void)testUpdateWithRefreshResultWhenAutoRefreshIsNotAllowed { - GACAppCheckTokenRefresher *refresher = [self createRefresher]; - - __auto_type newRefreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:[NSDate dateWithTimeIntervalSinceNow:60 * 60] - receivedAtDate:self.initialTokenRefreshResult.tokenExpirationDate]; - - // 1. Expect checking if auto-refresh allowed before scheduling initial refresh. - [[[self.mockSettings expect] andReturnValue:@(NO)] isTokenAutoRefreshEnabled]; - - // 2. Don't expect timer to be scheduled. - XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"]; - timerCreateExpectation.inverted = YES; - - __auto_type weakSelf = self; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - weakSelf.fakeTimer.createHandler = nil; - [timerCreateExpectation fulfill]; - }; - - // 3. Update token expiration date. - [refresher updateWithRefreshResult:newRefreshResult]; - - // 4. Wait for timer to be created. - [self waitForExpectations:@[ timerCreateExpectation ] timeout:1]; - - OCMVerifyAll(self.mockSettings); -} - -- (void)testUpdateWithRefreshResult_WhenTokenExpiresLessThanIn1Minute { - GACAppCheckTokenRefresher *refresher = [self createRefresher]; - - NSDate *newExpirationDate = [NSDate dateWithTimeIntervalSinceNow:0.5 * 60]; - __auto_type newRefreshResult = [[GACAppCheckTokenRefreshResult alloc] - initWithStatusSuccessAndExpirationDate:newExpirationDate - receivedAtDate:[NSDate date]]; - - // 1. Expect checking if auto-refresh allowed before scheduling refresh. - [[[self.mockSettings expect] andReturnValue:@(YES)] isTokenAutoRefreshEnabled]; - - // 2. Expect timer to be scheduled in at least 1 minute. - XCTestExpectation *timerCreateExpectation = [self expectationWithDescription:@"create timer"]; - - __auto_type weakSelf = self; - self.fakeTimer.createHandler = ^(NSDate *_Nonnull fireDate) { - weakSelf.fakeTimer.createHandler = nil; - - // 1 minute is the minimal interval between successful refreshes. - XCTAssert([GACDateTestUtils isDate:fireDate - approximatelyEqualCurrentPlusTimeInterval:60 - precision:1]); - [timerCreateExpectation fulfill]; - }; - - // 3. Update token expiration date. - [refresher updateWithRefreshResult:newRefreshResult]; - - // 4. Wait for timer to be created. - [self waitForExpectations:@[ timerCreateExpectation ] timeout:1]; - - OCMVerifyAll(self.mockSettings); -} - -#pragma mark - Helpers - -- (void)fireTimer { - if (self.fakeTimer.handler) { - self.fakeTimer.handler(); - } else { - XCTFail(@"handler must not be nil!"); - } -} - -- (GACAppCheckTokenRefresher *)createRefresher { - return [[GACAppCheckTokenRefresher alloc] initWithRefreshResult:self.initialTokenRefreshResult - timerProvider:[self.fakeTimer fakeTimerProvider] - settings:self.mockSettings]; -} - -- (NSDate *)expectedRefreshDateWithReceivedDate:(NSDate *)receivedDate - expirationDate:(NSDate *)expirationDate { - NSTimeInterval timeToLive = [expirationDate timeIntervalSinceDate:receivedDate]; - XCTAssertGreaterThanOrEqual(timeToLive, 0); - - NSTimeInterval timeToRefresh = timeToLive / 2 + 5 * 60; // 50% of TTL + 5 min - - NSTimeInterval minimalAutoRefreshInterval = 60; // 1 min - timeToRefresh = MAX(timeToRefresh, minimalAutoRefreshInterval); - - NSDate *refreshDate = [receivedDate dateByAddingTimeInterval:timeToRefresh]; - - NSDate *now = [NSDate date]; - - return [refreshDate laterDate:now]; -} - -@end diff --git a/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m b/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m deleted file mode 100644 index 4af5d3e84b36..000000000000 --- a/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m +++ /dev/null @@ -1,200 +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 - -#import -#import "FBLPromise+Testing.h" - -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheck.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -#import - -#import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h" -#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h" -#import "AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h" - -#import "AppCheckCore/Tests/Utils/URLSession/GACURLSessionOCMockStub.h" - -static NSString *const kResourceName = @"projects/test_project_id/apps/test_app_id"; - -@interface GACAppCheckDebugProviderAPIServiceTests : XCTestCase -@property(nonatomic) GACAppCheckDebugProviderAPIService *debugAPIService; - -@property(nonatomic) id mockAPIService; -@end - -@implementation GACAppCheckDebugProviderAPIServiceTests - -- (void)setUp { - [super setUp]; - - self.mockAPIService = OCMProtocolMock(@protocol(GACAppCheckAPIServiceProtocol)); - OCMStub([self.mockAPIService baseURL]).andReturn(@"https://test.appcheck.url.com/alpha"); - - self.debugAPIService = - [[GACAppCheckDebugProviderAPIService alloc] initWithAPIService:self.mockAPIService - resourceName:kResourceName]; -} - -- (void)tearDown { - self.debugAPIService = nil; - [self.mockAPIService stopMocking]; - self.mockAPIService = nil; - [super tearDown]; -} - -- (void)testAppCheckTokenSuccess { - NSString *debugToken = [NSUUID UUID].UUIDString; - GACAppCheckToken *expectedResult = [[GACAppCheckToken alloc] initWithToken:@"app_check_token" - expirationDate:[NSDate date]]; - - // 1. Stub API service. - // 1.1. Stub API response. - NSString *expectedRequestURL = - [NSString stringWithFormat:@"%@%@", [self.mockAPIService baseURL], - @"/projects/test_project_id/apps/test_app_id:exchangeDebugToken"]; - id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) { - XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL); - return YES; - }]; - - id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDebugToken:debugToken]; - NSData *fakeResponseData = [@"fake response" dataUsingEncoding:NSUTF8StringEncoding]; - NSHTTPURLResponse *HTTPResponse = [GACURLSessionOCMockStub HTTPResponseWithCode:200]; - GULURLSessionDataResponse *APIResponse = - [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:fakeResponseData]; - - 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.debugAPIService appCheckTokenWithDebugToken:debugToken]; - - // 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 { - NSString *debugToken = [NSUUID UUID].UUIDString; - NSError *parsingError = [NSError errorWithDomain:@"testAppCheckTokenResponseParsingError" - code:-1 - userInfo:nil]; - - // 1. Stub API service. - // 1.1. Stub API response. - NSString *expectedRequestURL = - [NSString stringWithFormat:@"%@%@", [self.mockAPIService baseURL], - @"/projects/test_project_id/apps/test_app_id:exchangeDebugToken"]; - id URLValidationArg = [OCMArg checkWithBlock:^BOOL(NSURL *URL) { - XCTAssertEqualObjects(URL.absoluteString, expectedRequestURL); - return YES; - }]; - - id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDebugToken:debugToken]; - NSData *fakeResponseData = [@"fake response" dataUsingEncoding:NSUTF8StringEncoding]; - NSHTTPURLResponse *HTTPResponse = [GACURLSessionOCMockStub HTTPResponseWithCode:200]; - GULURLSessionDataResponse *APIResponse = - [[GULURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:fakeResponseData]; - - 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.debugAPIService appCheckTokenWithDebugToken:debugToken]; - - // 3. Verify. - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - - XCTAssertTrue(tokenPromise.isRejected); - XCTAssertEqualObjects(tokenPromise.error, parsingError); - XCTAssertNil(tokenPromise.value); - - OCMVerifyAll(self.mockAPIService); -} - -- (void)testAppCheckTokenNetworkError { - NSString *debugToken = [NSUUID UUID].UUIDString; - NSError *APIError = [NSError errorWithDomain:@"testAppCheckTokenNetworkError" - code:-1 - userInfo:nil]; - - // 1. Stub API service. - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise reject:APIError]; - - id HTTPBodyValidationArg = [self HTTPBodyValidationArgWithDebugToken:debugToken]; - OCMExpect([self.mockAPIService sendRequestWithURL:[OCMArg any] - HTTPMethod:@"POST" - body:HTTPBodyValidationArg - additionalHeaders:@{@"Content-Type" : @"application/json"}]) - .andReturn(rejectedPromise); - - // 2. Send request. - __auto_type tokenPromise = [self.debugAPIService appCheckTokenWithDebugToken:debugToken]; - - // 3. Verify. - XCTAssert(FBLWaitForPromisesWithTimeout(1)); - - XCTAssertTrue(tokenPromise.isRejected); - XCTAssertNil(tokenPromise.value); - XCTAssertEqualObjects(tokenPromise.error, APIError); - - OCMVerifyAll(self.mockAPIService); -} - -#pragma mark - Helpores - -- (id)HTTPBodyValidationArgWithDebugToken:(NSString *)debugToken { - return [OCMArg checkWithBlock:^BOOL(NSData *body) { - NSDictionary *decodedData = [NSJSONSerialization JSONObjectWithData:body - options:0 - error:nil]; - XCTAssert([decodedData isKindOfClass:[NSDictionary class]]); - - NSString *decodeDebugToken = decodedData[@"debug_token"]; - XCTAssertNotNil(decodeDebugToken); - XCTAssertEqualObjects(decodeDebugToken, debugToken); - return YES; - }]; -} - -@end diff --git a/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderTests.m b/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderTests.m deleted file mode 100644 index 99503da3ee00..000000000000 --- a/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderTests.m +++ /dev/null @@ -1,181 +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 - -#import -#import "FBLPromise+Testing.h" - -#import "AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckDebugProvider.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" - -#import "FirebaseCore/Extension/FirebaseCoreInternal.h" - -static NSString *const kDebugTokenEnvKey = @"FIRAAppCheckDebugToken"; -static NSString *const kDebugTokenUserDefaultsKey = @"FIRAAppCheckDebugToken"; - -@interface GACAppCheckDebugProvider (Tests) - -- (instancetype)initWithAPIService:(id)APIService; - -@end - -@interface GACAppCheckDebugProviderTests : XCTestCase - -@property(nonatomic) GACAppCheckDebugProvider *provider; -@property(nonatomic) id processInfoMock; -@property(nonatomic) id fakeAPIService; - -@end - -typedef void (^GACAppCheckTokenValidationBlock)(GACAppCheckToken *_Nullable token, - NSError *_Nullable error); - -@implementation GACAppCheckDebugProviderTests - -- (void)setUp { - self.processInfoMock = OCMPartialMock([NSProcessInfo processInfo]); - - self.fakeAPIService = OCMProtocolMock(@protocol(GACAppCheckDebugProviderAPIServiceProtocol)); - self.provider = [[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService]; -} - -- (void)tearDown { - self.provider = nil; - [self.processInfoMock stopMocking]; - self.processInfoMock = nil; - [[NSUserDefaults standardUserDefaults] removeObjectForKey:kDebugTokenUserDefaultsKey]; -} - -#pragma mark - Initialization - -- (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]; - - XCTAssertNotNil([[GACAppCheckDebugProvider alloc] - initWithServiceName:options.googleAppID - resourceName:[GACAppCheckDebugProviderTests resourceNameFromApp:app] - APIKey:app.options.APIKey - requestHooks:nil]); -} - -#pragma mark - Debug token generating/storing - -- (void)testCurrentTokenWhenEnvironmentVariableSetAndTokenStored { - [[NSUserDefaults standardUserDefaults] setObject:@"stored token" - forKey:kDebugTokenUserDefaultsKey]; - NSString *envToken = @"env token"; - OCMStub([self.processInfoMock processInfo]).andReturn(self.processInfoMock); - OCMExpect([self.processInfoMock environment]).andReturn(@{kDebugTokenEnvKey : envToken}); - - XCTAssertEqualObjects([self.provider currentDebugToken], envToken); -} - -- (void)testCurrentTokenWhenNoEnvironmentVariableAndTokenStored { - NSString *storedToken = @"stored token"; - [[NSUserDefaults standardUserDefaults] setObject:storedToken forKey:kDebugTokenUserDefaultsKey]; - - XCTAssertNil(NSProcessInfo.processInfo.environment[kDebugTokenEnvKey]); - - XCTAssertEqualObjects([self.provider currentDebugToken], storedToken); -} - -- (void)testCurrentTokenWhenNoEnvironmentVariableAndNoTokenStored { - XCTAssertNil(NSProcessInfo.processInfo.environment[kDebugTokenEnvKey]); - XCTAssertNil([[NSUserDefaults standardUserDefaults] stringForKey:kDebugTokenUserDefaultsKey]); - - NSString *generatedToken = [self.provider currentDebugToken]; - XCTAssertNotNil(generatedToken); - - // Check if the generated token is stored to the user defaults. - XCTAssertEqualObjects( - [[NSUserDefaults standardUserDefaults] stringForKey:kDebugTokenUserDefaultsKey], - generatedToken); - - // Check if the same token is used once generated. - XCTAssertEqualObjects([self.provider currentDebugToken], generatedToken); -} - -#pragma mark - Debug token to FAC token exchange - -- (void)testGetTokenSuccess { - // 1. Stub API service. - NSString *expectedDebugToken = [self.provider currentDebugToken]; - GACAppCheckToken *validToken = [[GACAppCheckToken alloc] initWithToken:@"valid_token" - expirationDate:[NSDate date] - receivedAtDate:[NSDate date]]; - OCMExpect([self.fakeAPIService appCheckTokenWithDebugToken:expectedDebugToken]) - .andReturn([FBLPromise resolvedWith:validToken]); - - // 2. Validate get token. - [self validateGetToken:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - XCTAssertNil(error); - XCTAssertEqualObjects(token.token, validToken.token); - XCTAssertEqualObjects(token.expirationDate, validToken.expirationDate); - XCTAssertEqualObjects(token.receivedAtDate, validToken.receivedAtDate); - }]; - - // 3. Verify fakes. - OCMVerifyAll(self.fakeAPIService); -} - -- (void)testGetTokenAPIError { - // 1. Stub API service. - NSString *expectedDebugToken = [self.provider currentDebugToken]; - NSError *APIError = [NSError errorWithDomain:@"testGetTokenAPIError" code:-1 userInfo:nil]; - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise reject:APIError]; - OCMExpect([self.fakeAPIService appCheckTokenWithDebugToken:expectedDebugToken]) - .andReturn(rejectedPromise); - - // 2. Validate get token. - [self validateGetToken:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - XCTAssertEqualObjects(error, APIError); - XCTAssertNil(token); - }]; - - // 3. Verify fakes. - OCMVerifyAll(self.fakeAPIService); -} - -#pragma mark - Helpers - -- (void)validateGetToken:(GACAppCheckTokenValidationBlock)validationBlock { - XCTestExpectation *expectation = [self expectationWithDescription:@"getToken"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - validationBlock(token, error); - [expectation fulfill]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:0.5]; -} - -// TODO(andrewheard): Remove from generic App Check SDK. -// FIREBASE_APP_CHECK_ONLY_BEGIN - -+ (NSString *)resourceNameFromApp:(FIRApp *)app { - return [NSString - stringWithFormat:@"projects/%@/apps/%@", app.options.projectID, app.options.googleAppID]; -} - -// FIREBASE_APP_CHECK_ONLY_END - -@end diff --git a/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckProviderTests.m b/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckProviderTests.m deleted file mode 100644 index dcea1c125fd5..000000000000 --- a/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckProviderTests.m +++ /dev/null @@ -1,275 +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 - -#import -#import "FBLPromise+Testing.h" - -#import "AppCheckCore/Sources/DeviceCheckProvider/API/GACDeviceCheckAPIService.h" -#import "AppCheckCore/Sources/DeviceCheckProvider/GACDeviceCheckTokenGenerator.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h" -#import "AppCheckCore/Sources/Public/AppCheckCore/GACDeviceCheckProvider.h" - -#import "FirebaseCore/Extension/FirebaseCoreInternal.h" - -#import "AppCheckCore/Tests/Utils/AppCheckBackoffWrapperFake/GACAppCheckBackoffWrapperFake.h" - -#if GAC_DEVICE_CHECK_SUPPORTED_TARGETS - -GAC_DEVICE_CHECK_PROVIDER_AVAILABILITY -@interface GACDeviceCheckProvider (Tests) - -- (instancetype)initWithAPIService:(id)APIService - deviceTokenGenerator:(id)deviceTokenGenerator - backoffWrapper:(id)backoffWrapper; - -@end - -GAC_DEVICE_CHECK_PROVIDER_AVAILABILITY -@interface GACDeviceCheckProviderTests : XCTestCase - -@property(nonatomic) GACDeviceCheckProvider *provider; -@property(nonatomic) id fakeAPIService; -@property(nonatomic) id fakeTokenGenerator; -@property(nonatomic) GACAppCheckBackoffWrapperFake *fakeBackoffWrapper; - -@end - -@implementation GACDeviceCheckProviderTests - -- (void)setUp { - [super setUp]; - - self.fakeAPIService = OCMProtocolMock(@protocol(GACDeviceCheckAPIServiceProtocol)); - self.fakeTokenGenerator = OCMProtocolMock(@protocol(GACDeviceCheckTokenGenerator)); - - self.fakeBackoffWrapper = [[GACAppCheckBackoffWrapperFake alloc] init]; - // Don't backoff by default. - self.fakeBackoffWrapper.isNextOperationAllowed = YES; - - self.provider = [[GACDeviceCheckProvider alloc] initWithAPIService:self.fakeAPIService - deviceTokenGenerator:self.fakeTokenGenerator - backoffWrapper:self.fakeBackoffWrapper]; -} - -- (void)tearDown { - self.provider = nil; - self.fakeAPIService = nil; - self.fakeTokenGenerator = nil; - self.fakeBackoffWrapper = 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]; - - XCTAssertNotNil([[GACDeviceCheckProvider alloc] - initWithServiceName:app.name - resourceName:[GACDeviceCheckProviderTests resourceNameFromApp:app] - APIKey:app.options.APIKey - requestHooks:nil]); -} - -- (void)testGetTokenSuccess { - // 1. Expect device token to be generated. - NSData *deviceToken = [NSData data]; - id generateTokenArg = [OCMArg invokeBlockWithArgs:deviceToken, [NSNull null], nil]; - OCMExpect([self.fakeTokenGenerator generateTokenWithCompletionHandler:generateTokenArg]); - - // 2. Expect FAA token to be requested. - GACAppCheckToken *validToken = [[GACAppCheckToken alloc] initWithToken:@"valid_token" - expirationDate:[NSDate distantFuture] - receivedAtDate:[NSDate date]]; - OCMExpect([self.fakeAPIService appCheckTokenWithDeviceToken:deviceToken]) - .andReturn([FBLPromise resolvedWith:validToken]); - - // 3. Expect backoff wrapper to be used. - self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"]; - - // 4. Call getToken and validate the result. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - XCTAssertEqualObjects(token.token, validToken.token); - XCTAssertEqualObjects(token.expirationDate, validToken.expirationDate); - XCTAssertEqualObjects(token.receivedAtDate, validToken.receivedAtDate); - XCTAssertNil(error); - }]; - - [self waitForExpectations:@[ self.fakeBackoffWrapper.backoffExpectation, completionExpectation ] - timeout:0.5 - enforceOrder:YES]; - - // 5. Verify. - XCTAssertNil(self.fakeBackoffWrapper.operationError); - GACAppCheckToken *wrapperResult = - [self.fakeBackoffWrapper.operationResult isKindOfClass:[GACAppCheckToken class]] - ? self.fakeBackoffWrapper.operationResult - : nil; - XCTAssertEqualObjects(wrapperResult.token, validToken.token); - - OCMVerifyAll(self.fakeAPIService); - OCMVerifyAll(self.fakeTokenGenerator); -} - -- (void)testGetTokenWhenDeviceTokenFails { - NSError *deviceTokenError = [NSError errorWithDomain:@"GACDeviceCheckProviderTests" - 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 = ^GACAppCheckBackoffType(NSError *_Nonnull error) { - XCTAssertEqualObjects(error, deviceTokenError); - [errorHandlerExpectation fulfill]; - return GACAppCheckBackoffType1Day; - }; - - // 1. Expect device token to be generated. - id generateTokenArg = [OCMArg invokeBlockWithArgs:[NSNull null], deviceTokenError, nil]; - OCMExpect([self.fakeTokenGenerator generateTokenWithCompletionHandler:generateTokenArg]); - - // 2. Don't expect FAA token to be requested. - OCMReject([self.fakeAPIService appCheckTokenWithDeviceToken:[OCMArg any]]); - - // 3. Call getToken and validate the result. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - XCTAssertNil(token); - XCTAssertEqualObjects(error, deviceTokenError); - }]; - - [self waitForExpectations:@[ - self.fakeBackoffWrapper.backoffExpectation, errorHandlerExpectation, completionExpectation - ] - timeout:0.5 - enforceOrder:YES]; - - // 4. Verify. - OCMVerifyAll(self.fakeAPIService); - OCMVerifyAll(self.fakeTokenGenerator); - - XCTAssertEqualObjects(self.fakeBackoffWrapper.operationError, deviceTokenError); - XCTAssertNil(self.fakeBackoffWrapper.operationResult); -} - -- (void)testGetTokenWhenAPIServiceFails { - NSError *APIServiceError = [NSError errorWithDomain:@"GACDeviceCheckProviderTests" - 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 = ^GACAppCheckBackoffType(NSError *_Nonnull error) { - XCTAssertEqualObjects(error, APIServiceError); - [errorHandlerExpectation fulfill]; - return GACAppCheckBackoffType1Day; - }; - - // 1. Expect device token to be generated. - NSData *deviceToken = [NSData data]; - id generateTokenArg = [OCMArg invokeBlockWithArgs:deviceToken, [NSNull null], nil]; - OCMExpect([self.fakeTokenGenerator generateTokenWithCompletionHandler:generateTokenArg]); - - // 2. Expect FAA token to be requested. - FBLPromise *rejectedPromise = [FBLPromise pendingPromise]; - [rejectedPromise reject:APIServiceError]; - OCMExpect([self.fakeAPIService appCheckTokenWithDeviceToken:deviceToken]) - .andReturn(rejectedPromise); - - // 3. Call getToken and validate the result. - XCTestExpectation *completionExpectation = - [self expectationWithDescription:@"completionExpectation"]; - [self.provider - getTokenWithCompletion:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) { - [completionExpectation fulfill]; - XCTAssertNil(token); - XCTAssertEqualObjects(error, APIServiceError); - }]; - - [self waitForExpectations:@[ - self.fakeBackoffWrapper.backoffExpectation, errorHandlerExpectation, completionExpectation - ] - timeout:0.5 - enforceOrder:YES]; - - // 4. 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:^(GACAppCheckToken *_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); -} - -#pragma mark - Helpers - -// TODO(andrewheard): Remove from generic App Check SDK. -// FIREBASE_APP_CHECK_ONLY_BEGIN - -+ (NSString *)resourceNameFromApp:(FIRApp *)app { - return [NSString - stringWithFormat:@"projects/%@/apps/%@", app.options.projectID, app.options.googleAppID]; -} - -// FIREBASE_APP_CHECK_ONLY_END - -@end - -#endif // GAC_DEVICE_CHECK_SUPPORTED_TARGETS diff --git a/AppCheckCore/Tests/Unit/Swift/AppCheckAPITests.swift b/AppCheckCore/Tests/Unit/Swift/AppCheckAPITests.swift deleted file mode 100644 index 92ac64c98f1e..000000000000 --- a/AppCheckCore/Tests/Unit/Swift/AppCheckAPITests.swift +++ /dev/null @@ -1,208 +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. -// - -// MARK: This file is used to evaluate the experience of using Firebase APIs in Swift. - -import Foundation - -import AppCheckCore -import FirebaseCore - -final class AppCheckAPITests { - func usage() { - let app = FirebaseApp.app()! - let projectID = app.options.projectID! - let resourceName = "projects/\(projectID)/\(app.options.googleAppID)" - - // MARK: - AppAttestProvider - - #if TARGET_OS_IOS - if #available(iOS 14.0, *) { - // TODO(andrewheard): Add `requestHooks` in API tests. - if let provider = AppCheckCoreAppAttestProvider( - storageID: app.name, - resourceName: resourceName, - apiKey: app.options.apiKey, - keychainAccessGroup: nil, - requestHooks: nil - ) { - provider.getToken { token, error in - // ... - } - } - } - #endif // TARGET_OS_IOS - - // MARK: - AppCheck - - guard let app = FirebaseApp.app() else { return } - - // Retrieving an AppCheck instance - let appCheck = AppCheckCore( - serviceName: app.name, - resourceName: resourceName, - appCheckProvider: DummyAppCheckProvider(), - settings: DummyAppCheckSettings(), - tokenDelegate: DummyAppCheckTokenDelegate(), - keychainAccessGroup: app.options.appGroupID - ) - - // Get token - appCheck.token(forcingRefresh: false) { token, error in - if let _ /* error */ = error { - // ... - } else if let _ /* token */ = token { - // ... - } - } - - // Get token (async/await) - if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) { - // async/await is only available on iOS 13+ - Task { - do { - try await appCheck.token(forcingRefresh: false) - } catch { - // ... - } - } - } - - // MARK: - `AppCheckDebugProvider` - - // `AppCheckDebugProvider` initializer - // TODO(andrewheard): Add `requestHooks` in API tests. - let debugProvider = AppCheckCoreDebugProvider( - serviceName: app.name, - resourceName: resourceName, - apiKey: app.options.apiKey, - requestHooks: nil - ) - // Get token - debugProvider.getToken { token, error in - if let _ /* error */ = error { - // ... - } else if let _ /* token */ = token { - // ... - } - } - - // Get token (async/await) - if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) { - // async/await is only available on iOS 13+ - Task { - do { - _ = try await debugProvider.getToken() - } catch { - // ... - } - } - } - - _ = debugProvider.localDebugToken() - _ = debugProvider.currentDebugToken() - - // MARK: - AppCheckToken - - let token = AppCheckCoreToken(token: "token", expirationDate: Date.distantFuture) - _ = token.token - _ = token.expirationDate - - // MARK: - AppCheckErrors - - appCheck.token(forcingRefresh: false) { _, error in - if let error = error { - switch error { - case AppCheckCoreErrorCode.unknown: - break - case AppCheckCoreErrorCode.serverUnreachable: - break - case AppCheckCoreErrorCode.invalidConfiguration: - break - case AppCheckCoreErrorCode.keychain: - break - case AppCheckCoreErrorCode.unsupported: - break - default: - break - } - } - // ... - } - - // MARK: - AppCheckProvider - - // A protocol implemented by: - // - `AppAttestDebugProvider` - // - `AppCheckDebugProvider` - // - `DeviceCheckProvider` - - // MARK: - DeviceCheckProvider - - // `DeviceCheckProvider` initializer - #if !os(watchOS) - if #available(iOS 11.0, macOS 10.15, macCatalyst 13.0, tvOS 11.0, *) { - // TODO(andrewheard): Add `requestHooks` in API tests. - let deviceCheckProvider = AppCheckCoreDeviceCheckProvider( - serviceName: app.name, - resourceName: resourceName, - apiKey: app.options.apiKey, - requestHooks: nil - ) - // Get token - deviceCheckProvider.getToken { token, error in - if let _ /* error */ = error { - // ... - } else if let _ /* token */ = token { - // ... - } - } - // Get token (async/await) - if #available(iOS 13.0, tvOS 13.0, *) { - // async/await is only available on iOS 13+ - Task { - do { - _ = try await deviceCheckProvider.getToken() - } catch AppCheckCoreErrorCode.unsupported { - // ... - } catch { - // ... - } - } - } - } - #endif // !os(watchOS) - - // MARK: - AppCheckCoreLogger - - // Set the log level for App Check Core - AppCheckCoreLogger.logLevel = .debug - } -} - -class DummyAppCheckProvider: NSObject, AppCheckCoreProvider { - func getToken(completion handler: @escaping (AppCheckCoreToken?, Error?) -> Void) { - handler(AppCheckCoreToken(token: "token", expirationDate: .distantFuture), nil) - } -} - -class DummyAppCheckSettings: NSObject, AppCheckCoreSettingsProtocol { - var isTokenAutoRefreshEnabled: Bool = true -} - -class DummyAppCheckTokenDelegate: NSObject, AppCheckCoreTokenDelegate { - func tokenDidUpdate(_ token: AppCheckCoreToken, serviceName: String) {} -} diff --git a/AppCheckCore/Tests/Unit/Utils/GACFakeTimer.h b/AppCheckCore/Tests/Unit/Utils/GACFakeTimer.h deleted file mode 100644 index 0b2a8864dfe9..000000000000 --- a/AppCheckCore/Tests/Unit/Utils/GACFakeTimer.h +++ /dev/null @@ -1,40 +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 - -#import "AppCheckCore/Sources/Core/TokenRefresh/GACAppCheckTimer.h" - -NS_ASSUME_NONNULL_BEGIN - -typedef void (^GACFakeTimerCreateHandler)(NSDate *fireDate); - -@interface GACFakeTimer : NSObject - -- (GACTimerProvider)fakeTimerProvider; - -/// `createHandler` is called each time the timer provider returned by `fakeTimerProvider` is asked -/// to create a timer. -@property(nonatomic, copy, nullable) GACFakeTimerCreateHandler createHandler; - -@property(nonatomic, copy, nullable) dispatch_block_t invalidationHandler; - -/// The timer handler passed in the timer provider returned by `fakeTimerProvider` method. -@property(nonatomic, copy, nullable) dispatch_block_t handler; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Tests/Unit/Utils/GACFakeTimer.m b/AppCheckCore/Tests/Unit/Utils/GACFakeTimer.m deleted file mode 100644 index ae0884d12479..000000000000 --- a/AppCheckCore/Tests/Unit/Utils/GACFakeTimer.m +++ /dev/null @@ -1,39 +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 "AppCheckCore/Tests/Unit/Utils/GACFakeTimer.h" - -@implementation GACFakeTimer - -- (GACTimerProvider)fakeTimerProvider { - return ^id _Nullable(NSDate *fireDate, dispatch_queue_t queue, - dispatch_block_t handler) { - self.handler = handler; - if (self.createHandler) { - self.createHandler(fireDate); - } - - return self; - }; -} - -- (void)invalidate { - if (self.invalidationHandler) { - self.invalidationHandler(); - } -} - -@end diff --git a/AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.h b/AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.h deleted file mode 100644 index bc1212dcbe64..000000000000 --- a/AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.h +++ /dev/null @@ -1,25 +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 - -NS_ASSUME_NONNULL_BEGIN - -@interface GACFixtureLoader : NSObject - -+ (NSData *)loadFixtureNamed:(NSString *)fileName; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.m b/AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.m deleted file mode 100644 index ae603473ce3a..000000000000 --- a/AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.m +++ /dev/null @@ -1,54 +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 "AppCheckCore/Tests/Unit/Utils/GACFixtureLoader.h" - -@implementation GACFixtureLoader - -+ (NSData *)loadFixtureNamed:(NSString *)fileName { - NSURL *fileURL; - __auto_type possibleResourceBundles = [self possibleResourceBundles]; - for (NSBundle *bundle in possibleResourceBundles) { - fileURL = [bundle URLForResource:fileName withExtension:nil]; - if (fileURL != nil) { - NSLog(@"Fixture named: %@ was found at bundle %@", fileName, [bundle bundleIdentifier]); - break; - } - } - - NSError *error; - NSData *data = [NSData dataWithContentsOfURL:fileURL options:0 error:&error]; - - return data; -} - -+ (NSArray *)possibleResourceBundles { - NSBundle *bundleForClass = [NSBundle bundleForClass:[self class]]; - - // Swift Package Manager packages resources into separate bundles inside the test bundle. - NSArray *enclosedBundleURLs = [bundleForClass URLsForResourcesWithExtension:@"bundle" - subdirectory:nil]; - - NSMutableArray *bundlesForResources = [@[ bundleForClass ] mutableCopy]; - for (NSURL *bundleURL in enclosedBundleURLs) { - NSBundle *bundle = [NSBundle bundleWithURL:bundleURL]; - if (bundle) { - [bundlesForResources addObject:bundle]; - } - } - - return [bundlesForResources copy]; -} - -@end diff --git a/AppCheckCore/Tests/Utils/Date/GACDateTestUtils.h b/AppCheckCore/Tests/Utils/Date/GACDateTestUtils.h deleted file mode 100644 index 2445e992bf9c..000000000000 --- a/AppCheckCore/Tests/Utils/Date/GACDateTestUtils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 "FIRPersistentCacheIndexManager.h" - -#import - -#include -#include "Firestore/core/src/api/persistent_cache_index_manager.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FIRPersistentCacheIndexManager (/* Init */) - -- (instancetype)initWithPersistentCacheIndexManager: - (std::shared_ptr)indexManager - NS_DESIGNATED_INITIALIZER; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AppCheckCore/Tests/Utils/Date/GACDateTestUtils.m b/AppCheckCore/Tests/Utils/Date/GACDateTestUtils.m deleted file mode 100644 index dbfc417e97b7..000000000000 --- a/AppCheckCore/Tests/Utils/Date/GACDateTestUtils.m +++ /dev/null @@ -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 "AppCheckCore/Tests/Utils/Date/GACDateTestUtils.h" - -#import - -@implementation GACDateTestUtils - -+ (BOOL)isDate:(NSDate *)date - approximatelyEqualCurrentPlusTimeInterval:(NSTimeInterval)timeInterval - precision:(NSTimeInterval)precision { - NSDate *expectedDate = [NSDate dateWithTimeIntervalSinceNow:timeInterval]; - return ABS([date timeIntervalSinceDate:expectedDate]) <= precision; -} - -@end diff --git a/AppCheckCore/Tests/Utils/URLSession/GACURLSessionOCMockStub.m b/AppCheckCore/Tests/Utils/URLSession/GACURLSessionOCMockStub.m deleted file mode 100644 index 537e9717ad6a..000000000000 --- a/AppCheckCore/Tests/Utils/URLSession/GACURLSessionOCMockStub.m +++ /dev/null @@ -1,65 +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 "AppCheckCore/Tests/Utils/URLSession/GACURLSessionOCMockStub.h" - -#import - -@implementation GACURLSessionOCMockStub - -+ (id)stubURLSessionDataTaskWithResponse:(NSHTTPURLResponse *)response - body:(NSData *)body - error:(NSError *)error - URLSessionMock:(id)URLSessionMock - requestValidationBlock:(FIRRequestValidationBlock)requestValidationBlock { - id mockDataTask = OCMStrictClassMock([NSURLSessionDataTask class]); - - // Validate request content. - FIRRequestValidationBlock nonOptionalRequestValidationBlock = - requestValidationBlock ?: ^BOOL(id request) { - return YES; - }; - - id URLRequestValidationArg = [OCMArg checkWithBlock:nonOptionalRequestValidationBlock]; - - // Save task completion to be called on the `[NSURLSessionDataTask resume]` - __block void (^taskCompletion)(NSData *, NSURLResponse *, NSError *); - id completionArg = [OCMArg checkWithBlock:^BOOL(id obj) { - taskCompletion = obj; - return YES; - }]; - - // Expect `dataTaskWithRequest` to be called. - OCMExpect([URLSessionMock dataTaskWithRequest:URLRequestValidationArg - completionHandler:completionArg]) - .andReturn(mockDataTask); - - // Expect the task to be resumed and call the task completion. - OCMExpect([(NSURLSessionDataTask *)mockDataTask resume]).andDo(^(NSInvocation *invocation) { - taskCompletion(body, response, error); - }); - - return mockDataTask; -} - -+ (NSHTTPURLResponse *)HTTPResponseWithCode:(NSInteger)statusCode { - return [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"http://localhost"] - statusCode:statusCode - HTTPVersion:@"HTTP/1.1" - headerFields:nil]; -} - -@end