diff --git a/.github/workflows/_spm.yml b/.github/workflows/_spm.yml index d4c8bf96c43..01f84ff11b2 100644 --- a/.github/workflows/_spm.yml +++ b/.github/workflows/_spm.yml @@ -58,6 +58,14 @@ on: required: false default: false + # Custom environment variables to inject into the jobs. + # Expected to be a JSON-formatted string. + # Example: '{"FIREBASE_APP_CHECK_BRANCH": "nc/target-split"}' + env_vars: + type: string + required: false + default: "{}" + outputs: cache_key: description: "The cache key for the Swift package resolution." @@ -72,6 +80,24 @@ jobs: cache_key: ${{ steps.generate_cache_key.outputs.cache_key }} steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set Custom Environment Variables + run: | + python3 -c ' + import os, json + try: + env_vars = json.loads(os.environ.get("CUSTOM_ENV_VARS", "{}")) + if not isinstance(env_vars, dict): + raise ValueError("env_vars must be a JSON object") + with open(os.environ["GITHUB_ENV"], "a") as f: + for k, v in env_vars.items(): + f.write(f"{k}={v}\n") + except json.JSONDecodeError: + print("Warning: env_vars is not valid JSON. Skipping.") + except Exception as e: + print(f"Error setting env vars: {e}") + ' + env: + CUSTOM_ENV_VARS: ${{ inputs.env_vars }} - name: Xcode run: sudo xcode-select -s /Applications/Xcode_26.4.app/Contents/Developer - name: Generate Swift Package.resolved @@ -110,6 +136,24 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false + - name: Set Custom Environment Variables + run: | + python3 -c ' + import os, json + try: + env_vars = json.loads(os.environ.get("CUSTOM_ENV_VARS", "{}")) + if not isinstance(env_vars, dict): + raise ValueError("env_vars must be a JSON object") + with open(os.environ["GITHUB_ENV"], "a") as f: + for k, v in env_vars.items(): + f.write(f"{k}={v}\n") + except json.JSONDecodeError: + print("Warning: env_vars is not valid JSON. Skipping.") + except Exception as e: + print(f"Error setting env vars: {e}") + ' + env: + CUSTOM_ENV_VARS: ${{ inputs.env_vars }} - uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: .build diff --git a/.github/workflows/sdk.appcheck.yml b/.github/workflows/sdk.appcheck.yml index c47ff92a2c0..f2ac1424a40 100644 --- a/.github/workflows/sdk.appcheck.yml +++ b/.github/workflows/sdk.appcheck.yml @@ -30,6 +30,8 @@ jobs: uses: ./.github/workflows/_spm.yml with: target: ${{ matrix.target }} + env_vars: '{"FIREBASE_APP_CHECK_BRANCH": "nc/target-split"}' + catalyst: uses: ./.github/workflows/_catalyst.yml diff --git a/FirebaseAppCheck/CHANGELOG.md b/FirebaseAppCheck/CHANGELOG.md index 7cd810bfbc5..57b0e1bbb45 100644 --- a/FirebaseAppCheck/CHANGELOG.md +++ b/FirebaseAppCheck/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- [added] Added reCAPTCHA Enterprise provider. + # 10.27.0 - [fixed] [CocoaPods] missing symbol error for FIRGetLoggerLevel. (#12899) diff --git a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h index 3f2fce59da5..3394fd3fc69 100644 --- a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h +++ b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h @@ -35,6 +35,10 @@ FOUNDATION_EXPORT NSString *const kFIRLoggerAppCheckMessageCodeDebugToken; // FIRDeviceCheckProvider.m FOUNDATION_EXPORT NSString *const kFIRLoggerAppCheckMessageDeviceCheckProviderIncompleteFIROptions; +// FIRRecaptchaEnterpriseProvider.m +FOUNDATION_EXPORT NSString *const + kFIRLoggerAppCheckMessageRecaptchaEnterpriseProviderIncompleteFIROptions; + void FIRAppCheckDebugLog(NSString *messageCode, NSString *message, ...); GACAppCheckLogLevel FIRGetGACAppCheckLogLevel(void); diff --git a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m index 3c001b22326..61efd520478 100644 --- a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m +++ b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m @@ -35,6 +35,10 @@ // FIRDeviceCheckProvider.m NSString *const kFIRLoggerAppCheckMessageDeviceCheckProviderIncompleteFIROptions = @"I-FAA006001"; +// FIRRecaptchaEnterpriseProvider.m +NSString *const kFIRLoggerAppCheckMessageRecaptchaEnterpriseProviderIncompleteFIROptions = + @"I-FAA007001"; + #pragma mark - Log functions void FIRAppCheckDebugLog(NSString *messageCode, NSString *message, ...) { diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h new file mode 100644 index 00000000000..fba4981ab31 --- /dev/null +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h @@ -0,0 +1,29 @@ +/* + * Copyright 2026 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 "FIRAppCheckProviderFactory.h" + +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(AppCheckRecaptchaEnterpriseProviderFactory) +@interface FIRAppCheckRecaptchaEnterpriseProviderFactory : NSObject + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithSiteKey:(NSString *)siteKey; + +@end +NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h new file mode 100644 index 00000000000..cd416838ef0 --- /dev/null +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h @@ -0,0 +1,32 @@ +/* + * Copyright 2026 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 "FIRAppCheckAvailability.h" +#import "FIRAppCheckProvider.h" + +@class FIRApp; + +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(RecaptchaEnterpriseProvider) +@interface FIRRecaptchaEnterpriseProvider : NSObject + +- (instancetype)init NS_UNAVAILABLE; +- (nullable instancetype)initWithApp:(FIRApp *)app siteKey:(NSString *)siteKey; + +@end +NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FirebaseAppCheck.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FirebaseAppCheck.h index 807336e29b4..bc7869f6798 100644 --- a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FirebaseAppCheck.h +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FirebaseAppCheck.h @@ -30,3 +30,7 @@ // App Attest provider. #import "FIRAppAttestProvider.h" + +// Recaptcha Enterprise provider +#import "FIRAppCheckRecaptchaEnterpriseProviderFactory.h" +#import "FIRRecaptchaEnterpriseProvider.h" diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m new file mode 100644 index 00000000000..918e0c9bb02 --- /dev/null +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m @@ -0,0 +1,44 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h" + +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheck.h" +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h" + +@interface FIRAppCheckRecaptchaEnterpriseProviderFactory () + +@property(nonatomic, readonly) NSString *siteKey; + +@end + +@implementation FIRAppCheckRecaptchaEnterpriseProviderFactory + +- (instancetype)initWithSiteKey:(NSString *)siteKey { + NSParameterAssert(siteKey.length > 0); + self = [super init]; + + if (self) { + _siteKey = [siteKey copy]; + } + return self; +} + +- (nullable id)createProviderWithApp:(nonnull FIRApp *)app { + return [[FIRRecaptchaEnterpriseProvider alloc] initWithApp:app siteKey:self.siteKey]; +} + +@end diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m new file mode 100644 index 00000000000..d5fb456089b --- /dev/null +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m @@ -0,0 +1,106 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h" +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckAvailability.h" + +#import + +@import RecaptchaEnterpriseProvider; + +#import "FirebaseAppCheck/Sources/Core/FIRApp+AppCheck.h" + +#import "FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h" +#import "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.h" +#import "FirebaseAppCheck/Sources/Core/FIRAppCheckValidator.h" +#import "FirebaseAppCheck/Sources/Core/FIRHeartbeatLogger+AppCheck.h" + +#import "FirebaseCore/Extension/FirebaseCoreInternal.h" + +@interface FIRRecaptchaEnterpriseProvider () + +@property(nonatomic, readonly) GACRecaptchaEnterpriseProvider *recaptchaEnterpriseProvider; + +@end + +@implementation FIRRecaptchaEnterpriseProvider + +- (instancetype)initWithRecaptchaEnterpriseProvider: + (GACRecaptchaEnterpriseProvider *)recaptchaEnterpriseProvider { + self = [super init]; + if (self) { + _recaptchaEnterpriseProvider = recaptchaEnterpriseProvider; + } + return self; +} + +- (nullable instancetype)initWithApp:(FIRApp *)app siteKey:(NSString *)siteKey { + if (siteKey.length == 0) { + return nil; + } + NSArray *missingOptionsFields = + [FIRAppCheckValidator tokenExchangeMissingFieldsInOptions:app.options]; + if (missingOptionsFields.count > 0) { + FIRLogError(kFIRLoggerAppCheck, + kFIRLoggerAppCheckMessageRecaptchaEnterpriseProviderIncompleteFIROptions, + @"Cannot instantiate `FIRRecaptchaEnterpriseProvider` for app: %@. The following " + @"`FirebaseOptions` fields are missing: %@", + app.name, [missingOptionsFields componentsJoinedByString:@", "]); + return nil; + } + + id heartbeatHook = [app.heartbeatLogger requestHook]; + GACRecaptchaEnterpriseProvider *recaptchaEnterpriseProvider = + [[GACRecaptchaEnterpriseProvider alloc] + initWithSiteKey:siteKey + resourceName:app.resourceName + APIKey:app.options.APIKey + requestHooks:heartbeatHook ? @[ heartbeatHook ] : @[]]; + + return [self initWithRecaptchaEnterpriseProvider:recaptchaEnterpriseProvider]; +} + +#pragma mark - FIRAppCheckProvider + +- (void)getTokenWithCompletion:(void (^)(FIRAppCheckToken *_Nullable token, + NSError *_Nullable error))handler { + [self.recaptchaEnterpriseProvider + getTokenWithCompletion:^(GACAppCheckToken *_Nullable internalToken, + NSError *_Nullable error) { + if (error) { + handler(nil, error); + return; + } + + handler([[FIRAppCheckToken alloc] initWithInternalToken:internalToken], nil); + }]; +} + +- (void)getLimitedUseTokenWithCompletion:(void (^)(FIRAppCheckToken *_Nullable, + NSError *_Nullable))handler { + [self.recaptchaEnterpriseProvider + getLimitedUseTokenWithCompletion:^(GACAppCheckToken *_Nullable internalToken, + NSError *_Nullable error) { + if (error) { + handler(nil, error); + return; + } + + handler([[FIRAppCheckToken alloc] initWithInternalToken:internalToken], nil); + }]; +} + +@end diff --git a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m new file mode 100644 index 00000000000..b8a79e41f8d --- /dev/null +++ b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m @@ -0,0 +1,45 @@ +/* + * Copyright 2026 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 + +#import "FirebaseCore/Extension/FirebaseCoreInternal.h" + +@interface FIRAppCheckRecaptchaEnterpriseProviderFactoryTests : XCTestCase +@end + +@implementation FIRAppCheckRecaptchaEnterpriseProviderFactoryTests + +- (void)testCreateProviderWithApp { + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"app_id" GCMSenderID:@"sender_id"]; + options.APIKey = @"api_key"; + options.projectID = @"project_id"; + FIRApp *app = [[FIRApp alloc] initInstanceWithName:@"testCreateProviderWithApp" options:options]; + app.dataCollectionDefaultEnabled = NO; + + NSString *siteKey = @"test_site_key"; + FIRAppCheckRecaptchaEnterpriseProviderFactory *factory = + [[FIRAppCheckRecaptchaEnterpriseProviderFactory alloc] initWithSiteKey:siteKey]; + + FIRRecaptchaEnterpriseProvider *createdProvider = [factory createProviderWithApp:app]; + + XCTAssert([createdProvider isKindOfClass:[FIRRecaptchaEnterpriseProvider class]]); +} + +@end diff --git a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m new file mode 100644 index 00000000000..ba53801e7b2 --- /dev/null +++ b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m @@ -0,0 +1,164 @@ +/* + * Copyright 2026 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 +@import RecaptchaEnterpriseProvider; + +#import +#import "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.h" + +#import "FirebaseCore/Extension/FirebaseCoreInternal.h" + +static NSString *const kAppName = @"test_app_name"; +static NSString *const kAppID = @"test_app_id"; +static NSString *const kAPIKey = @"test_api_key"; +static NSString *const kProjectID = @"test_project_id"; +static NSString *const kProjectNumber = @"123456789"; +static NSString *const kSiteKey = @"test_site_key"; + +@interface FIRRecaptchaEnterpriseProvider (Tests) + +- (instancetype)initWithRecaptchaEnterpriseProvider: + (GACRecaptchaEnterpriseProvider *)recaptchaEnterpriseProvider; + +@end + +@interface FIRRecaptchaEnterpriseProviderTests : XCTestCase + +@property(nonatomic) id recaptchaEnterpriseProviderMock; +@property(nonatomic) FIRRecaptchaEnterpriseProvider *provider; + +@end + +@implementation FIRRecaptchaEnterpriseProviderTests + +- (void)setUp { + [super setUp]; + + self.recaptchaEnterpriseProviderMock = OCMStrictClassMock([GACRecaptchaEnterpriseProvider class]); + self.provider = [[FIRRecaptchaEnterpriseProvider alloc] + initWithRecaptchaEnterpriseProvider:self.recaptchaEnterpriseProviderMock]; +} + +- (void)tearDown { + self.provider = nil; + [self.recaptchaEnterpriseProviderMock stopMocking]; + self.recaptchaEnterpriseProviderMock = nil; + [super tearDown]; +} + +- (void)testInitWithValidApp { + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kAppID GCMSenderID:kProjectNumber]; + options.APIKey = kAPIKey; + options.projectID = kProjectID; + FIRApp *app = [[FIRApp alloc] initInstanceWithName:kAppName options:options]; + app.dataCollectionDefaultEnabled = NO; + + XCTAssertNotNil([[FIRRecaptchaEnterpriseProvider alloc] initWithApp:app siteKey:kSiteKey]); +} + +- (void)testInitWithIncompleteApp { + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kAppID GCMSenderID:kProjectNumber]; + options.projectID = kProjectID; + FIRApp *missingAPIKeyApp = [[FIRApp alloc] initInstanceWithName:kAppName options:options]; + missingAPIKeyApp.dataCollectionDefaultEnabled = NO; + + XCTAssertNil([[FIRRecaptchaEnterpriseProvider alloc] initWithApp:missingAPIKeyApp + siteKey:kSiteKey]); + + options.projectID = nil; + options.APIKey = kAPIKey; + FIRApp *missingProjectIDApp = [[FIRApp alloc] initInstanceWithName:kAppName options:options]; + missingProjectIDApp.dataCollectionDefaultEnabled = NO; + XCTAssertNil([[FIRRecaptchaEnterpriseProvider alloc] initWithApp:missingProjectIDApp + siteKey:kSiteKey]); +} + +- (void)testGetTokenSuccess { + NSDate *date = [NSDate date]; + GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"valid_token" + expirationDate:date + receivedAtDate:date]; + OCMExpect([self.recaptchaEnterpriseProviderMock + getTokenWithCompletion:([OCMArg + invokeBlockWithArgs:validInternalToken, [NSNull null], nil])]); + + [self.provider + getTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) { + XCTAssertEqualObjects(token.token, validInternalToken.token); + XCTAssertEqualObjects(token.expirationDate, validInternalToken.expirationDate); + XCTAssertEqualObjects(token.receivedAtDate, validInternalToken.receivedAtDate); + XCTAssertNil(error); + }]; + + OCMVerifyAll(self.recaptchaEnterpriseProviderMock); +} + +- (void)testGetTokenAPIError { + NSError *expectedError = [NSError errorWithDomain:@"testGetTokenAPIError" code:-1 userInfo:nil]; + OCMExpect([self.recaptchaEnterpriseProviderMock + getTokenWithCompletion:([OCMArg invokeBlockWithArgs:[NSNull null], expectedError, nil])]); + + [self.provider + getTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) { + XCTAssertNil(token); + XCTAssertEqualObjects(error, expectedError); + }]; + + OCMVerifyAll(self.recaptchaEnterpriseProviderMock); +} + +- (void)testGetLimitedUseTokenSuccess { + NSDate *date = [NSDate date]; + GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"TEST_ValidToken" + expirationDate:date + receivedAtDate:date]; + OCMExpect([self.recaptchaEnterpriseProviderMock + getLimitedUseTokenWithCompletion:([OCMArg invokeBlockWithArgs:validInternalToken, + [NSNull null], nil])]); + + [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, + NSError *_Nullable error) { + XCTAssertEqualObjects(token.token, validInternalToken.token); + XCTAssertEqualObjects(token.expirationDate, validInternalToken.expirationDate); + XCTAssertEqualObjects(token.receivedAtDate, validInternalToken.receivedAtDate); + XCTAssertNil(error); + }]; + + OCMVerifyAll(self.recaptchaEnterpriseProviderMock); +} + +- (void)testGetLimitedUseTokenProviderError { + NSError *expectedError = [NSError errorWithDomain:@"TEST_LimitedUseToken_Error" + code:-1 + userInfo:nil]; + OCMExpect([self.recaptchaEnterpriseProviderMock + getLimitedUseTokenWithCompletion:([OCMArg invokeBlockWithArgs:[NSNull null], expectedError, + nil])]); + + [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, + NSError *_Nullable error) { + XCTAssertNil(token); + XCTAssertEqualObjects(error, expectedError); + }]; + + OCMVerifyAll(self.recaptchaEnterpriseProviderMock); +} + +@end diff --git a/Package.swift b/Package.swift index de53606344b..4921ce28cf3 100644 --- a/Package.swift +++ b/Package.swift @@ -172,8 +172,7 @@ let package = Package( url: "https://github.com/google/interop-ios-for-google-sdks.git", "101.0.0" ..< "102.0.0" ), - .package(url: "https://github.com/google/app-check.git", - "11.0.1" ..< "12.0.0"), + appCheckDependency(), ], targets: [ .target( @@ -1263,9 +1262,11 @@ let package = Package( "FirebaseAppCheckInterop", "FirebaseCore", .product(name: "AppCheckCore", package: "app-check"), + .product(name: "RecaptchaEnterpriseProvider", package: "app-check"), .product(name: "GULEnvironment", package: "GoogleUtilities"), .product(name: "GULUserDefaults", package: "GoogleUtilities"), ], + path: "FirebaseAppCheck/Sources", publicHeadersPath: "Public", cSettings: [ @@ -1667,3 +1668,17 @@ func isFoundationModelsSupportedPlatformSwiftSetting() -> SwiftSetting { .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS]) ) } + +func appCheckDependency() -> Package.Dependency { + let appCheckURL = "https://github.com/google/app-check.git" + + if let localPath = Context.environment["FIREBASE_APP_CHECK_LOCAL_PATH"] { + return .package(path: localPath) + } + + if let branch = Context.environment["FIREBASE_APP_CHECK_BRANCH"] { + return .package(url: appCheckURL, branch: branch) + } + + return .package(url: appCheckURL, "11.0.1" ..< "12.0.0") +}