From 4be9ef3def1a721ad695d6a2806847527a0819d0 Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Fri, 1 May 2026 15:03:48 -0400 Subject: [PATCH 01/18] feat(AppCheck): implement FIRRecaptchaEnterpriseProvider and tests --- .../Sources/Core/FIRAppCheckLogger.h | 4 + .../Sources/Core/FIRAppCheckLogger.m | 4 + .../FIRRecaptchaEnterpriseProvider.h | 16 ++ .../FIRRecaptchaEnterpriseProviderFactory.h | 13 ++ .../FIRRecaptchaEnterpriseProvider.m | 102 +++++++++++ .../FIRRecaptchaEnterpriseProviderFactory.m | 46 +++++ ...RRecaptchaEnterpriseProviderFactoryTests.m | 44 +++++ .../FIRRecaptchaEnterpriseProviderTests.m | 161 ++++++++++++++++++ Package.swift | 3 +- 9 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h create mode 100644 FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h create mode 100644 FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m create mode 100644 FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m create mode 100644 FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m create mode 100644 FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m diff --git a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h index 3f2fce59da5..9f479b1c7f7 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..e683bdca606 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/FIRRecaptchaEnterpriseProvider.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h new file mode 100644 index 00000000000..c3bada74d6b --- /dev/null +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h @@ -0,0 +1,16 @@ +#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/FIRRecaptchaEnterpriseProviderFactory.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h new file mode 100644 index 00000000000..e2ac9a3ac6f --- /dev/null +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h @@ -0,0 +1,13 @@ +#import +#import "FIRAppCheckProviderFactory.h" + +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(RecaptchaEnterpriseProviderFactory) +@interface FIRRecaptchaEnterpriseProviderFactory : NSObject + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithSiteKey:(NSString *)siteKey; + +@end +NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m new file mode 100644 index 00000000000..6b51a6f9922 --- /dev/null +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m @@ -0,0 +1,102 @@ +/* + * 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/FIRAppCheckAvailability.h" +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.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" + +NS_ASSUME_NONNULL_BEGIN + +@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 { + 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; + } + + GACRecaptchaEnterpriseProvider *recaptchaEnterpriseProvider = + [[GACRecaptchaEnterpriseProvider alloc] initWithSiteKey:siteKey + resourceName:app.resourceName + APIKey:app.options.APIKey + requestHooks:@[ [app.heartbeatLogger requestHook] ]]; + + 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 + +NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m new file mode 100644 index 00000000000..3dfcf15e195 --- /dev/null +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m @@ -0,0 +1,46 @@ +/* + * 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/FIRRecaptchaEnterpriseProviderFactory.h" + +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheck.h" +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRRecaptchaEnterpriseProviderFactory () + +@property(nonatomic, readonly) NSString *siteKey; + +@end + +@implementation FIRRecaptchaEnterpriseProviderFactory + +- (instancetype)initWithSiteKey:(NSString *)siteKey { + 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 + +NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m new file mode 100644 index 00000000000..591aca40af1 --- /dev/null +++ b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.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 + +#import +#import + +#import "FirebaseCore/Extension/FirebaseCoreInternal.h" + +@interface FIRRecaptchaEnterpriseProviderFactoryTests : XCTestCase +@end + +@implementation FIRRecaptchaEnterpriseProviderFactoryTests + +- (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"; + FIRRecaptchaEnterpriseProviderFactory *factory = [[FIRRecaptchaEnterpriseProviderFactory 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..f92cd844a9c --- /dev/null +++ b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m @@ -0,0 +1,161 @@ +/* + * 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 "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.h" +#import + +#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, copy) NSString *resourceName; +@property(nonatomic) id recaptchaEnterpriseProviderMock; +@property(nonatomic) FIRRecaptchaEnterpriseProvider *provider; + +@end + +@implementation FIRRecaptchaEnterpriseProviderTests + +- (void)setUp { + [super setUp]; + + self.resourceName = [NSString stringWithFormat:@"projects/%@/apps/%@", kProjectID, kAppID]; + 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 { + GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"valid_token" + expirationDate:[NSDate date] + receivedAtDate:[NSDate 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 { + GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"TEST_ValidToken" + expirationDate:[NSDate date] + receivedAtDate:[NSDate 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); + XCTAssertIdentical(error, expectedError); + }]; + + OCMVerifyAll(self.recaptchaEnterpriseProviderMock); +} + +@end diff --git a/Package.swift b/Package.swift index de53606344b..a82e36e4a26 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"), + .package(path: "/Users/nickcooke/Developer/app-check"), ], targets: [ .target( From 6b4f4c4140d8e7049a20b3c3518971a5710b41a5 Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Fri, 1 May 2026 15:40:53 -0400 Subject: [PATCH 02/18] style, changelog, nits --- FirebaseAppCheck/CHANGELOG.md | 3 ++ .../Sources/Core/FIRAppCheckLogger.h | 4 +- .../Sources/Core/FIRAppCheckLogger.m | 4 +- .../FIRRecaptchaEnterpriseProvider.h | 16 ++++++++ .../FIRRecaptchaEnterpriseProviderFactory.h | 16 ++++++++ .../FIRRecaptchaEnterpriseProvider.m | 37 +++++++++---------- .../FIRRecaptchaEnterpriseProviderFactory.m | 4 -- Package.swift | 2 +- 8 files changed, 58 insertions(+), 28 deletions(-) diff --git a/FirebaseAppCheck/CHANGELOG.md b/FirebaseAppCheck/CHANGELOG.md index 7cd810bfbc5..66d2eeeb4d7 100644 --- a/FirebaseAppCheck/CHANGELOG.md +++ b/FirebaseAppCheck/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- [added] Added `FIRRecaptchaEnterpriseProvider` wrapping reCAPTCHA Enterprise for iOS. + # 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 9f479b1c7f7..3394fd3fc69 100644 --- a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h +++ b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h @@ -36,8 +36,8 @@ FOUNDATION_EXPORT NSString *const kFIRLoggerAppCheckMessageCodeDebugToken; FOUNDATION_EXPORT NSString *const kFIRLoggerAppCheckMessageDeviceCheckProviderIncompleteFIROptions; // FIRRecaptchaEnterpriseProvider.m -FOUNDATION_EXPORT NSString *const kFIRLoggerAppCheckMessageRecaptchaEnterpriseProviderIncompleteFIROptions; - +FOUNDATION_EXPORT NSString *const + kFIRLoggerAppCheckMessageRecaptchaEnterpriseProviderIncompleteFIROptions; void FIRAppCheckDebugLog(NSString *messageCode, NSString *message, ...); diff --git a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m index e683bdca606..61efd520478 100644 --- a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m +++ b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m @@ -36,8 +36,8 @@ NSString *const kFIRLoggerAppCheckMessageDeviceCheckProviderIncompleteFIROptions = @"I-FAA006001"; // FIRRecaptchaEnterpriseProvider.m -NSString *const kFIRLoggerAppCheckMessageRecaptchaEnterpriseProviderIncompleteFIROptions = @"I-FAA007001"; - +NSString *const kFIRLoggerAppCheckMessageRecaptchaEnterpriseProviderIncompleteFIROptions = + @"I-FAA007001"; #pragma mark - Log functions diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h index c3bada74d6b..cd416838ef0 100644 --- a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h @@ -1,3 +1,19 @@ +/* + * 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" diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h index e2ac9a3ac6f..5ee0a1ecdd5 100644 --- a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h @@ -1,3 +1,19 @@ +/* + * 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" diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m index 6b51a6f9922..bc4bf1d8d7c 100644 --- a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m @@ -14,8 +14,8 @@ * limitations under the License. */ -#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckAvailability.h" #import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h" +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckAvailability.h" #import @@ -29,8 +29,6 @@ #import "FirebaseCore/Extension/FirebaseCoreInternal.h" -NS_ASSUME_NONNULL_BEGIN - @interface FIRRecaptchaEnterpriseProvider () @property(nonatomic, readonly) GACRecaptchaEnterpriseProvider *recaptchaEnterpriseProvider; @@ -39,7 +37,8 @@ @interface FIRRecaptchaEnterpriseProvider () @implementation FIRRecaptchaEnterpriseProvider -- (instancetype)initWithRecaptchaEnterpriseProvider:(GACRecaptchaEnterpriseProvider *)recaptchaEnterpriseProvider { +- (instancetype)initWithRecaptchaEnterpriseProvider: + (GACRecaptchaEnterpriseProvider *)recaptchaEnterpriseProvider { self = [super init]; if (self) { _recaptchaEnterpriseProvider = recaptchaEnterpriseProvider; @@ -60,10 +59,11 @@ - (nullable instancetype)initWithApp:(FIRApp *)app siteKey:(NSString *)siteKey { } GACRecaptchaEnterpriseProvider *recaptchaEnterpriseProvider = - [[GACRecaptchaEnterpriseProvider alloc] initWithSiteKey:siteKey - resourceName:app.resourceName - APIKey:app.options.APIKey - requestHooks:@[ [app.heartbeatLogger requestHook] ]]; + [[GACRecaptchaEnterpriseProvider alloc] + initWithSiteKey:siteKey + resourceName:app.resourceName + APIKey:app.options.APIKey + requestHooks:@[ [app.heartbeatLogger requestHook] ]]; return [self initWithRecaptchaEnterpriseProvider:recaptchaEnterpriseProvider]; } @@ -72,15 +72,16 @@ - (nullable instancetype)initWithApp:(FIRApp *)app siteKey:(NSString *)siteKey { - (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); - }]; + [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, @@ -98,5 +99,3 @@ - (void)getLimitedUseTokenWithCompletion:(void (^)(FIRAppCheckToken *_Nullable, } @end - -NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m index 3dfcf15e195..84579d30f4a 100644 --- a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m @@ -19,8 +19,6 @@ #import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheck.h" #import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h" -NS_ASSUME_NONNULL_BEGIN - @interface FIRRecaptchaEnterpriseProviderFactory () @property(nonatomic, readonly) NSString *siteKey; @@ -42,5 +40,3 @@ - (instancetype)initWithSiteKey:(NSString *)siteKey { } @end - -NS_ASSUME_NONNULL_END diff --git a/Package.swift b/Package.swift index a82e36e4a26..dec61ee92a6 100644 --- a/Package.swift +++ b/Package.swift @@ -172,7 +172,7 @@ let package = Package( url: "https://github.com/google/interop-ios-for-google-sdks.git", "101.0.0" ..< "102.0.0" ), - .package(path: "/Users/nickcooke/Developer/app-check"), + .package(url: "https://github.com/google/app-check.git", branch: "nc/target-split"), ], targets: [ .target( From cbc93334c62977c7a8e567cd8668d3db150e57c8 Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Fri, 1 May 2026 15:41:53 -0400 Subject: [PATCH 03/18] changelog update --- FirebaseAppCheck/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseAppCheck/CHANGELOG.md b/FirebaseAppCheck/CHANGELOG.md index 66d2eeeb4d7..57b0e1bbb45 100644 --- a/FirebaseAppCheck/CHANGELOG.md +++ b/FirebaseAppCheck/CHANGELOG.md @@ -1,5 +1,5 @@ # Unreleased -- [added] Added `FIRRecaptchaEnterpriseProvider` wrapping reCAPTCHA Enterprise for iOS. +- [added] Added reCAPTCHA Enterprise provider. # 10.27.0 - [fixed] [CocoaPods] missing symbol error for FIRGetLoggerLevel. (#12899) From 455549f0cadcf73872bde9c31dee18bb2c088c24 Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Fri, 1 May 2026 16:40:08 -0400 Subject: [PATCH 04/18] refactor(AppCheck): align factory name with spec and fix review comments --- ...pCheckRecaptchaEnterpriseProviderFactory.h | 29 +++++++++++++++++++ ...CheckRecaptchaEnterpriseProviderFactory.m} | 8 ++--- .../FIRRecaptchaEnterpriseProvider.m | 4 ++- ...RRecaptchaEnterpriseProviderFactoryTests.m | 9 +++--- .../FIRRecaptchaEnterpriseProviderTests.m | 25 ++++++++-------- Package.swift | 16 +++++++++- 6 files changed, 69 insertions(+), 22 deletions(-) create mode 100644 FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h rename FirebaseAppCheck/Sources/RecaptchaProvider/{FIRRecaptchaEnterpriseProviderFactory.m => FIRAppCheckRecaptchaEnterpriseProviderFactory.m} (78%) diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h new file mode 100644 index 00000000000..49f65af6457 --- /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; +- (nullable instancetype)initWithSiteKey:(NSString *)siteKey; + +@end +NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m similarity index 78% rename from FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m rename to FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m index 84579d30f4a..38419fcc006 100644 --- a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactory.m +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m @@ -14,20 +14,20 @@ * limitations under the License. */ -#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h" +#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h" #import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheck.h" #import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h" -@interface FIRRecaptchaEnterpriseProviderFactory () +@interface FIRAppCheckRecaptchaEnterpriseProviderFactory () @property(nonatomic, readonly) NSString *siteKey; @end -@implementation FIRRecaptchaEnterpriseProviderFactory +@implementation FIRAppCheckRecaptchaEnterpriseProviderFactory -- (instancetype)initWithSiteKey:(NSString *)siteKey { +- (nullable instancetype)initWithSiteKey:(NSString *)siteKey { self = [super init]; if (self) { _siteKey = [siteKey copy]; diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m index bc4bf1d8d7c..1f06a25c976 100644 --- a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m @@ -22,6 +22,7 @@ @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" @@ -58,12 +59,13 @@ - (nullable instancetype)initWithApp:(FIRApp *)app siteKey:(NSString *)siteKey { return nil; } + id heartbeatHook = [app.heartbeatLogger requestHook]; GACRecaptchaEnterpriseProvider *recaptchaEnterpriseProvider = [[GACRecaptchaEnterpriseProvider alloc] initWithSiteKey:siteKey resourceName:app.resourceName APIKey:app.options.APIKey - requestHooks:@[ [app.heartbeatLogger requestHook] ]]; + requestHooks:heartbeatHook ? @[ heartbeatHook ] : @[]]; return [self initWithRecaptchaEnterpriseProvider:recaptchaEnterpriseProvider]; } diff --git a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m index 591aca40af1..b8a79e41f8d 100644 --- a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m +++ b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m @@ -16,15 +16,15 @@ #import +#import #import -#import #import "FirebaseCore/Extension/FirebaseCoreInternal.h" -@interface FIRRecaptchaEnterpriseProviderFactoryTests : XCTestCase +@interface FIRAppCheckRecaptchaEnterpriseProviderFactoryTests : XCTestCase @end -@implementation FIRRecaptchaEnterpriseProviderFactoryTests +@implementation FIRAppCheckRecaptchaEnterpriseProviderFactoryTests - (void)testCreateProviderWithApp { FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"app_id" GCMSenderID:@"sender_id"]; @@ -34,7 +34,8 @@ - (void)testCreateProviderWithApp { app.dataCollectionDefaultEnabled = NO; NSString *siteKey = @"test_site_key"; - FIRRecaptchaEnterpriseProviderFactory *factory = [[FIRRecaptchaEnterpriseProviderFactory alloc] initWithSiteKey:siteKey]; + FIRAppCheckRecaptchaEnterpriseProviderFactory *factory = + [[FIRAppCheckRecaptchaEnterpriseProviderFactory alloc] initWithSiteKey:siteKey]; FIRRecaptchaEnterpriseProvider *createdProvider = [factory createProviderWithApp:app]; diff --git a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m index f92cd844a9c..bab69187d4a 100644 --- a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m +++ b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m @@ -14,14 +14,14 @@ * limitations under the License. */ -#import #import +#import #import @import RecaptchaEnterpriseProvider; -#import "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.h" #import +#import "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.h" #import "FirebaseCore/Extension/FirebaseCoreInternal.h" @@ -34,13 +34,13 @@ @interface FIRRecaptchaEnterpriseProvider (Tests) -- (instancetype)initWithRecaptchaEnterpriseProvider:(GACRecaptchaEnterpriseProvider *)recaptchaEnterpriseProvider; +- (instancetype)initWithRecaptchaEnterpriseProvider: + (GACRecaptchaEnterpriseProvider *)recaptchaEnterpriseProvider; @end @interface FIRRecaptchaEnterpriseProviderTests : XCTestCase -@property(nonatomic, copy) NSString *resourceName; @property(nonatomic) id recaptchaEnterpriseProviderMock; @property(nonatomic) FIRRecaptchaEnterpriseProvider *provider; @@ -51,10 +51,9 @@ @implementation FIRRecaptchaEnterpriseProviderTests - (void)setUp { [super setUp]; - self.resourceName = [NSString stringWithFormat:@"projects/%@/apps/%@", kProjectID, kAppID]; self.recaptchaEnterpriseProviderMock = OCMStrictClassMock([GACRecaptchaEnterpriseProvider class]); - self.provider = - [[FIRRecaptchaEnterpriseProvider alloc] initWithRecaptchaEnterpriseProvider:self.recaptchaEnterpriseProviderMock]; + self.provider = [[FIRRecaptchaEnterpriseProvider alloc] + initWithRecaptchaEnterpriseProvider:self.recaptchaEnterpriseProviderMock]; } - (void)tearDown { @@ -80,13 +79,15 @@ - (void)testInitWithIncompleteApp { FIRApp *missingAPIKeyApp = [[FIRApp alloc] initInstanceWithName:kAppName options:options]; missingAPIKeyApp.dataCollectionDefaultEnabled = NO; - XCTAssertNil([[FIRRecaptchaEnterpriseProvider alloc] initWithApp:missingAPIKeyApp siteKey:kSiteKey]); + 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]); + XCTAssertNil([[FIRRecaptchaEnterpriseProvider alloc] initWithApp:missingProjectIDApp + siteKey:kSiteKey]); } - (void)testGetTokenSuccess { @@ -131,7 +132,7 @@ - (void)testGetLimitedUseTokenSuccess { [NSNull null], nil])]); [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, - NSError *_Nullable error) { + NSError *_Nullable error) { XCTAssertEqualObjects(token.token, validInternalToken.token); XCTAssertEqualObjects(token.expirationDate, validInternalToken.expirationDate); XCTAssertEqualObjects(token.receivedAtDate, validInternalToken.receivedAtDate); @@ -150,9 +151,9 @@ - (void)testGetLimitedUseTokenProviderError { nil])]); [self.provider getLimitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, - NSError *_Nullable error) { + NSError *_Nullable error) { XCTAssertNil(token); - XCTAssertIdentical(error, expectedError); + XCTAssertEqualObjects(error, expectedError); }]; OCMVerifyAll(self.recaptchaEnterpriseProviderMock); diff --git a/Package.swift b/Package.swift index dec61ee92a6..e02d983b9bd 100644 --- a/Package.swift +++ b/Package.swift @@ -172,7 +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", branch: "nc/target-split"), + appCheckDependency(), ], targets: [ .target( @@ -1666,3 +1666,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") +} From 9d5f030228cd2a853e23d29cc48b06714cc673ec Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Fri, 1 May 2026 16:40:29 -0400 Subject: [PATCH 05/18] delete moved --- .../FIRRecaptchaEnterpriseProviderFactory.h | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h deleted file mode 100644 index 5ee0a1ecdd5..00000000000 --- a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProviderFactory.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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(RecaptchaEnterpriseProviderFactory) -@interface FIRRecaptchaEnterpriseProviderFactory : NSObject - -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithSiteKey:(NSString *)siteKey; - -@end -NS_ASSUME_NONNULL_END From ecc0b001b9da2c390f5920a7010a684f3cade84d Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Fri, 1 May 2026 17:23:03 -0400 Subject: [PATCH 06/18] feat(ci): ddd env_vars input to _spm.yml workflow --- .github/workflows/_spm.yml | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) 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 From 7bc430a48f3bf59440ac2abf0e54019a285692cc Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 1 May 2026 17:26:51 -0400 Subject: [PATCH 07/18] fixes --- FirebaseAppCheck/CHANGELOG.md | 3 - .../Sources/Core/FIRAppCheckLogger.m | 4 - ...pCheckRecaptchaEnterpriseProviderFactory.h | 29 ----- ...pCheckRecaptchaEnterpriseProviderFactory.m | 42 ------- .../FIRRecaptchaEnterpriseProvider.m | 103 ------------------ ...RRecaptchaEnterpriseProviderFactoryTests.m | 45 -------- Package.swift | 3 +- 7 files changed, 2 insertions(+), 227 deletions(-) diff --git a/FirebaseAppCheck/CHANGELOG.md b/FirebaseAppCheck/CHANGELOG.md index 57b0e1bbb45..7cd810bfbc5 100644 --- a/FirebaseAppCheck/CHANGELOG.md +++ b/FirebaseAppCheck/CHANGELOG.md @@ -1,6 +1,3 @@ -# Unreleased -- [added] Added reCAPTCHA Enterprise provider. - # 10.27.0 - [fixed] [CocoaPods] missing symbol error for FIRGetLoggerLevel. (#12899) diff --git a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m index 61efd520478..3c001b22326 100644 --- a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m +++ b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.m @@ -35,10 +35,6 @@ // 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 index 49f65af6457..e69de29bb2d 100644 --- a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h @@ -1,29 +0,0 @@ -/* - * 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; -- (nullable instancetype)initWithSiteKey:(NSString *)siteKey; - -@end -NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m index 38419fcc006..e69de29bb2d 100644 --- a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m @@ -1,42 +0,0 @@ -/* - * 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 - -- (nullable instancetype)initWithSiteKey:(NSString *)siteKey { - 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 index 1f06a25c976..e69de29bb2d 100644 --- a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m +++ b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m @@ -1,103 +0,0 @@ -/* - * 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 { - 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 index b8a79e41f8d..e69de29bb2d 100644 --- a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m +++ b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m @@ -1,45 +0,0 @@ -/* - * 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/Package.swift b/Package.swift index e02d983b9bd..62cab8681f9 100644 --- a/Package.swift +++ b/Package.swift @@ -172,7 +172,8 @@ let package = Package( url: "https://github.com/google/interop-ios-for-google-sdks.git", "101.0.0" ..< "102.0.0" ), - appCheckDependency(), + .package(url: "https://github.com/google/app-check.git", + "11.0.1" ..< "12.0.0"), ], targets: [ .target( From 54a399d73c0f0c5c37c04b1f3337124aa11e0901 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 1 May 2026 17:27:32 -0400 Subject: [PATCH 08/18] fixes --- .../Sources/Core/FIRAppCheckLogger.h | 4 --- .../FIRRecaptchaEnterpriseProvider.h | 32 ------------------- 2 files changed, 36 deletions(-) diff --git a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h index 3394fd3fc69..3f2fce59da5 100644 --- a/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h +++ b/FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h @@ -35,10 +35,6 @@ 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/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h index cd416838ef0..e69de29bb2d 100644 --- a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h @@ -1,32 +0,0 @@ -/* - * 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 From b1d018dc55e58d1b230a9ab6e310b78234a84105 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 1 May 2026 17:27:48 -0400 Subject: [PATCH 09/18] Delete FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h --- .../FIRAppCheckRecaptchaEnterpriseProviderFactory.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckRecaptchaEnterpriseProviderFactory.h deleted file mode 100644 index e69de29bb2d..00000000000 From 18588ba461d6825a16ab7df281d72d54df2f8214 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 1 May 2026 17:28:13 -0400 Subject: [PATCH 10/18] Delete FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h --- .../Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRRecaptchaEnterpriseProvider.h deleted file mode 100644 index e69de29bb2d..00000000000 From 23aef287850c3371beac49ce7a16a0029ef07231 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 1 May 2026 17:28:15 -0400 Subject: [PATCH 11/18] Delete FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m --- .../FIRAppCheckRecaptchaEnterpriseProviderFactory.m | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRAppCheckRecaptchaEnterpriseProviderFactory.m deleted file mode 100644 index e69de29bb2d..00000000000 From 1bac45e81bf04774830d6cfd9adcb8e46a8abe3c Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 1 May 2026 17:28:18 -0400 Subject: [PATCH 12/18] Delete FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m --- .../Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m diff --git a/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m b/FirebaseAppCheck/Sources/RecaptchaProvider/FIRRecaptchaEnterpriseProvider.m deleted file mode 100644 index e69de29bb2d..00000000000 From b445711c08188690006e8cc00ef558ecbfdda794 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 1 May 2026 17:28:21 -0400 Subject: [PATCH 13/18] Delete FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m --- .../FIRRecaptchaEnterpriseProviderFactoryTests.m | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m diff --git a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderFactoryTests.m deleted file mode 100644 index e69de29bb2d..00000000000 From 20aca1bfa238d1e503d51a7a8dbc9b6546f18f4c Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 1 May 2026 17:28:24 -0400 Subject: [PATCH 14/18] Delete FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m --- .../FIRRecaptchaEnterpriseProviderTests.m | 162 ------------------ 1 file changed, 162 deletions(-) delete mode 100644 FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m diff --git a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m b/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m deleted file mode 100644 index bab69187d4a..00000000000 --- a/FirebaseAppCheck/Tests/Unit/RecaptchaProvider/FIRRecaptchaEnterpriseProviderTests.m +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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 { - GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"valid_token" - expirationDate:[NSDate date] - receivedAtDate:[NSDate 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 { - GACAppCheckToken *validInternalToken = [[GACAppCheckToken alloc] initWithToken:@"TEST_ValidToken" - expirationDate:[NSDate date] - receivedAtDate:[NSDate 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 From 564e596d9155825563edc35577615d1e601e954e Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 1 May 2026 17:29:03 -0400 Subject: [PATCH 15/18] Apply suggestion from @ncooke3 --- Package.swift | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Package.swift b/Package.swift index 62cab8681f9..de53606344b 100644 --- a/Package.swift +++ b/Package.swift @@ -1667,17 +1667,3 @@ 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") -} From c5ec9727ddc0e35b3b6ee87daa54d14700c49d2f Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:25:42 -0400 Subject: [PATCH 16/18] Apply suggestions from code review Co-authored-by: Nick Cooke <36927374+ncooke3@users.noreply.github.com> --- .github/workflows/_spm.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/_spm.yml b/.github/workflows/_spm.yml index 01f84ff11b2..9830345f0de 100644 --- a/.github/workflows/_spm.yml +++ b/.github/workflows/_spm.yml @@ -88,9 +88,9 @@ jobs: 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") + with open(os.environ["GITHUB_ENV"], "a") as file: + for key, value in env_vars.items(): + file.write(f"{key}={value}\n") except json.JSONDecodeError: print("Warning: env_vars is not valid JSON. Skipping.") except Exception as e: @@ -144,9 +144,9 @@ jobs: 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") + with open(os.environ["GITHUB_ENV"], "a") as file: + for key, value in env_vars.items(): + file.write(f"{key}={value}\n") except json.JSONDecodeError: print("Warning: env_vars is not valid JSON. Skipping.") except Exception as e: From 0c28102f9d30fd85dd2497b68629439fcd338784 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:26:36 -0400 Subject: [PATCH 17/18] Update .github/workflows/_spm.yml --- .github/workflows/_spm.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_spm.yml b/.github/workflows/_spm.yml index 9830345f0de..37191554786 100644 --- a/.github/workflows/_spm.yml +++ b/.github/workflows/_spm.yml @@ -93,8 +93,8 @@ jobs: file.write(f"{key}={value}\n") except json.JSONDecodeError: print("Warning: env_vars is not valid JSON. Skipping.") - except Exception as e: - print(f"Error setting env vars: {e}") + except Exception as error: + print(f"Error setting env vars: {error}") ' env: CUSTOM_ENV_VARS: ${{ inputs.env_vars }} From 35a28821c4146a80fad458531e0f1ac83c7e0207 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:26:41 -0400 Subject: [PATCH 18/18] Update .github/workflows/_spm.yml --- .github/workflows/_spm.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_spm.yml b/.github/workflows/_spm.yml index 37191554786..c36af0017c4 100644 --- a/.github/workflows/_spm.yml +++ b/.github/workflows/_spm.yml @@ -149,8 +149,8 @@ jobs: file.write(f"{key}={value}\n") except json.JSONDecodeError: print("Warning: env_vars is not valid JSON. Skipping.") - except Exception as e: - print(f"Error setting env vars: {e}") + except Exception as error: + print(f"Error setting env vars: {error}") ' env: CUSTOM_ENV_VARS: ${{ inputs.env_vars }}