From 312d5068fc06138f6279dd512c3ec609533361db Mon Sep 17 00:00:00 2001 From: dmaclach Date: Fri, 19 May 2023 09:27:38 -0700 Subject: [PATCH 01/26] Fix up "double" stub (#298) Co-authored-by: mdmathias --- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index 59e3ac75..c64f1611 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -1494,7 +1494,7 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow authorizationResponse:SAVE_TO_ARG_BLOCK(updatedAuthorizationResponse) profileData:SAVE_TO_ARG_BLOCK(profileData)]; } else { - [[[_user stub] andReturn:_user] alloc]; + [[[_user expect] andReturn:_user] alloc]; (void)[[[_user expect] andReturn:_user] initWithAuthState:SAVE_TO_ARG_BLOCK(authState) profileData:SAVE_TO_ARG_BLOCK(profileData)]; } From b0bc583eb31df0354addb1e0df651fa6da1d386e Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 26 May 2023 15:11:30 -0700 Subject: [PATCH 02/26] Update to Swift 5 in GoogleSignInSwiftSupport.podspec (#317) --- GoogleSignInSwiftSupport.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GoogleSignInSwiftSupport.podspec b/GoogleSignInSwiftSupport.podspec index 75fa4866..dbe1d7bf 100644 --- a/GoogleSignInSwiftSupport.podspec +++ b/GoogleSignInSwiftSupport.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'GoogleSignInSwiftSupport' - s.version = '7.0.0' - s.swift_version = '4.0' + s.version = '7.0.1' + s.swift_version = '5.0' s.summary = 'Adds Swift-focused support for Google Sign-In.' s.description = 'Additional Swift support for the Google Sign-In SDK.' s.homepage = 'https://developers.google.com/identity/sign-in/ios/' From b45a83701141abde85a5e059a46b5ff039dca9ec Mon Sep 17 00:00:00 2001 From: Camden King <74014435+camden-king@users.noreply.github.com> Date: Thu, 30 Nov 2023 18:28:52 -0800 Subject: [PATCH 03/26] Clarifies that restored user is from local cache (#351) --- GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h index 5fa4dcdc..2576b13d 100644 --- a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h +++ b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h @@ -86,7 +86,9 @@ typedef NS_ERROR_ENUM(kGIDSignInErrorDomain, GIDSignInErrorCode) { /// @return `YES` if there is a previous user sign-in saved in keychain. - (BOOL)hasPreviousSignIn; -/// Attempts to restore a previous user sign-in without interaction. +/// Attempts to restore a previous user sign-in without interaction. +/// +/// Restores user from the local cache and refreshes tokens if they have expired (>1 hour). /// /// @param completion The block that is called on completion. This block will be called asynchronously /// on the main queue. From aa71a74f10f7d5fe0241c7ff661a60cf78ccc338 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Thu, 29 Feb 2024 14:32:21 -0800 Subject: [PATCH 04/26] Remove pod try from README (#372) --- README.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e3eb8d53..55fb6d94 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,9 @@ service. ## Getting Started Try either the [Objective-C](Samples/ObjC) or [Swift](Samples/Swift) sample app. -For example, to demo the Objective-C sample project, you have three options: +For example, to demo the Objective-C sample project, you have two options: -1. Using [CocoaPods](https://cocoapods.org/)'s `try` method: - -``` -pod try GoogleSignIn -``` - -Note, this will default to providing you with the Objective-C sample app. - -2. Using CocoaPod's `install` method: +1. Using CocoaPod's `install` method: ``` git clone https://github.com/google/GoogleSignIn-iOS @@ -35,7 +27,7 @@ pod install open SignInSampleForPod.xcworkspace ``` -3. Using [Swift Package Manager](https://swift.org/package-manager/): +2. Using [Swift Package Manager](https://swift.org/package-manager/): ``` git clone https://github.com/google/GoogleSignIn-iOS From 5da488f73365a57f37cffdfb1f619005d65e146a Mon Sep 17 00:00:00 2001 From: Yana Sang Date: Fri, 22 Mar 2024 09:14:19 -0700 Subject: [PATCH 05/26] Add Privacy Manifest (#382) --- GoogleSignIn.podspec | 10 +- .../Sources/Resources/PrivacyInfo.xcprivacy | 122 ++++++++++++++++++ .../Sources/Resources/PrivacyInfo.xcprivacy | 14 ++ GoogleSignInSwiftSupport.podspec | 3 + Package.swift | 13 +- 5 files changed, 152 insertions(+), 10 deletions(-) create mode 100644 GoogleSignIn/Sources/Resources/PrivacyInfo.xcprivacy create mode 100644 GoogleSignInSwift/Sources/Resources/PrivacyInfo.xcprivacy diff --git a/GoogleSignIn.podspec b/GoogleSignIn.podspec index d8407e90..1bae2140 100644 --- a/GoogleSignIn.podspec +++ b/GoogleSignIn.podspec @@ -33,9 +33,9 @@ The Google Sign-In SDK allows users to sign in with their Google account from th ] s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' - s.dependency 'AppAuth', '~> 1.6' - s.dependency 'GTMAppAuth', '~> 4.0' - s.dependency 'GTMSessionFetcher/Core', '>= 1.1', '< 4.0' + s.dependency 'AppAuth', '>= 1.7.3', '< 2.0' + s.dependency 'GTMAppAuth', '>= 4.1.1', '< 5.0' + s.dependency 'GTMSessionFetcher/Core', '~> 3.3' s.resource_bundle = { 'GoogleSignIn' => ['GoogleSignIn/Sources/{Resources,Strings}/*'] } @@ -55,7 +55,7 @@ The Google Sign-In SDK allows users to sign in with their Google account from th ] unit_tests.requires_app_host = true unit_tests.dependency 'OCMock' - unit_tests.dependency 'GoogleUtilities/MethodSwizzler', '~> 7.2' - unit_tests.dependency 'GoogleUtilities/SwizzlerTestHelpers', '~> 7.2' + unit_tests.dependency 'GoogleUtilities/MethodSwizzler', '~> 7.13' + unit_tests.dependency 'GoogleUtilities/SwizzlerTestHelpers', '~> 7.13' end end diff --git a/GoogleSignIn/Sources/Resources/PrivacyInfo.xcprivacy b/GoogleSignIn/Sources/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..010fc335 --- /dev/null +++ b/GoogleSignIn/Sources/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,122 @@ + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeName + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeEmailAddress + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypePhoneNumber + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeOtherDataTypes + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeCoarseLocation + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeUserID + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeDeviceID + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeOtherUsageData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + + + + diff --git a/GoogleSignInSwift/Sources/Resources/PrivacyInfo.xcprivacy b/GoogleSignInSwift/Sources/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..d37d6275 --- /dev/null +++ b/GoogleSignInSwift/Sources/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyCollectedDataTypes + + NSPrivacyAccessedAPITypes + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + diff --git a/GoogleSignInSwiftSupport.podspec b/GoogleSignInSwiftSupport.podspec index dbe1d7bf..9d4a5315 100644 --- a/GoogleSignInSwiftSupport.podspec +++ b/GoogleSignInSwiftSupport.podspec @@ -25,6 +25,9 @@ Pod::Spec.new do |s| 'SwiftUI', ] s.dependency 'GoogleSignIn', '~> 7.0' + s.resource_bundles = { + 'GoogleSignInSwiftSupport_Privacy' => 'GoogleSignInSwift/Sources/Resources/PrivacyInfo.xcprivacy' + } s.test_spec 'unit' do |unit_tests| unit_tests.platforms = { :ios => ios_deployment_target, diff --git a/Package.swift b/Package.swift index 8b498cc2..9c450811 100644 --- a/Package.swift +++ b/Package.swift @@ -44,15 +44,15 @@ let package = Package( .package( name: "AppAuth", url: "https://github.com/openid/AppAuth-iOS.git", - "1.6.0" ..< "2.0.0"), + from: "1.7.3"), .package( name: "GTMAppAuth", url: "https://github.com/google/GTMAppAuth.git", - from: "4.0.0"), + from: "4.1.1"), .package( name: "GTMSessionFetcher", url: "https://github.com/google/gtm-session-fetcher.git", - "1.5.0" ..< "4.0.0"), + from: "3.3.0"), .package( name: "OCMock", url: "https://github.com/firebase/ocmock.git", @@ -60,7 +60,7 @@ let package = Package( .package( name: "GoogleUtilities", url: "https://github.com/google/GoogleUtilities.git", - "7.3.0" ..< "8.0.0"), + from: "7.13.0"), ], targets: [ .target( @@ -95,7 +95,10 @@ let package = Package( dependencies: [ "GoogleSignIn", ], - path: "GoogleSignInSwift/Sources" + path: "GoogleSignInSwift/Sources", + resources: [ + .copy("Resources/PrivacyInfo.xcprivacy") + ] ), .testTarget( name: "GoogleSignIn-UnitTests", From a7965d134c5d3567026c523e0a8a583f73b62b0d Mon Sep 17 00:00:00 2001 From: Yana Sang Date: Tue, 26 Mar 2024 09:31:12 -0700 Subject: [PATCH 06/26] Prepare for 7.1.0 release (#386) --- CHANGELOG.md | 12 ++++++++++++ GoogleSignIn.podspec | 2 +- GoogleSignInSwiftSupport.podspec | 4 ++-- Package.swift | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b90ccfa..f46fa6e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 7.1.0 +- Update to Swift 5.0 in `GoogleSignInSwiftSupport` pod ([#317](https://github.com/google/GoogleSignIn-iOS/pull/317)) +- Documentation updates ([#351](https://github.com/google/GoogleSignIn-iOS/pull/351), [#372](https://github.com/google/GoogleSignIn-iOS/pull/372)) +- Add Privacy Manifest ([#382](https://github.com/google/GoogleSignIn-iOS/pull/382)) +- Internal + - Fix typo in `SFSafariViewController` ([#291](https://github.com/google/GoogleSignIn-iOS/pull/291)) + - Fix `OCMock` usage in unit test ([#298](https://github.com/google/GoogleSignIn-iOS/pull/298)) + - Use new [delegate protocol](https://github.com/google/GTMAppAuth/pull/224) from GTMAppAuth 4.0.0 ([#299](https://github.com/google/GoogleSignIn-iOS/pull/299)) + - Ensure that `completion` is not nil before calling `-[GIDSignIn restorePreviousSignIn:]` ([#301](https://github.com/google/GoogleSignIn-iOS/pull/301)) + - Removes `macos-11` runner in GitHub workflows ([#302](https://github.com/google/GoogleSignIn-iOS/pull/302)) + - Updates button name reference so UI automation tests pass ([#308](https://github.com/google/GoogleSignIn-iOS/pull/308)) + # 7.0.0 - All configuration can now be provided via your `Info.plist` file. ([#228](https://github.com/google/GoogleSignIn-iOS/pull/228)) - Use the following keys in `KEYVALUE` pairs to configure the SDK: diff --git a/GoogleSignIn.podspec b/GoogleSignIn.podspec index 1bae2140..c6d7c612 100644 --- a/GoogleSignIn.podspec +++ b/GoogleSignIn.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleSignIn' - s.version = '7.0.0' + s.version = '7.1.0' s.summary = 'Enables iOS apps to sign in with Google.' s.description = <<-DESC The Google Sign-In SDK allows users to sign in with their Google account from third-party apps. diff --git a/GoogleSignInSwiftSupport.podspec b/GoogleSignInSwiftSupport.podspec index 9d4a5315..f40df45e 100644 --- a/GoogleSignInSwiftSupport.podspec +++ b/GoogleSignInSwiftSupport.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleSignInSwiftSupport' - s.version = '7.0.1' + s.version = '7.1.0' s.swift_version = '5.0' s.summary = 'Adds Swift-focused support for Google Sign-In.' s.description = 'Additional Swift support for the Google Sign-In SDK.' @@ -24,7 +24,7 @@ Pod::Spec.new do |s| 'CoreGraphics', 'SwiftUI', ] - s.dependency 'GoogleSignIn', '~> 7.0' + s.dependency 'GoogleSignIn', '~> 7.1' s.resource_bundles = { 'GoogleSignInSwiftSupport_Privacy' => 'GoogleSignInSwift/Sources/Resources/PrivacyInfo.xcprivacy' } diff --git a/Package.swift b/Package.swift index 9c450811..8336f199 100644 --- a/Package.swift +++ b/Package.swift @@ -17,7 +17,7 @@ import PackageDescription -let googleSignInVersion = "7.0.0" +let googleSignInVersion = "7.1.0" let package = Package( name: "GoogleSignIn", From cce5accedb086db64ebc44318aa3e2887a87e745 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Thu, 20 Jul 2023 13:00:59 -0700 Subject: [PATCH 07/26] Rebase and add privacy manifest from main branch --- .gitignore | 3 + GoogleSignIn.podspec | 3 +- .../GIDAppCheck/API/GIDAppCheckProvider.h | 59 +++ .../GIDAppCheck/Implementations/GIDAppCheck.h | 38 ++ .../GIDAppCheck/Implementations/GIDAppCheck.m | 135 ++++++ .../UI/GIDActivityIndicatorViewController.h | 37 ++ .../UI/GIDActivityIndicatorViewController.m | 42 ++ .../API/GIDAppCheckTokenFetcher.h | 37 ++ .../FIRAppCheck+GIDAppCheckTokenFetcher.h | 29 ++ .../GIDAppCheckTokenFetcherFake.h | 45 ++ .../GIDAppCheckTokenFetcherFake.m | 51 ++ GoogleSignIn/Sources/GIDGoogleUser.m | 1 + GoogleSignIn/Sources/GIDSignIn.m | 193 ++++++-- GoogleSignIn/Sources/GIDSignIn_Private.h | 13 +- .../Public/GoogleSignIn/GIDAppCheckError.h | 35 ++ .../Sources/Public/GoogleSignIn/GIDSignIn.h | 29 +- .../Public/GoogleSignIn/GoogleSignIn.h | 3 + GoogleSignIn/Tests/Unit/GIDAppCheckTest.m | 185 ++++++++ GoogleSignIn/Tests/Unit/GIDSignInTest.m | 103 ++++- Package.swift | 8 +- .../project.pbxproj | 434 ++++++++++++++++++ .../xcschemes/AppAttestExample.xcscheme | 84 ++++ .../AppAttestExample.entitlements | 8 + .../AppAttestExampleApp.swift | 54 +++ .../BirthdayAppCheckProviderFactory.swift | 25 + .../AppAttestExample/BirthdayLoader.swift | 111 +++++ .../AppAttestExample/ContentView.swift | 218 +++++++++ .../Assets/AccentColor.colorset/Contents.json | 11 + .../Assets/AppIcon.appiconset/Contents.json | 13 + .../Assets/Contents.json | 6 + .../Preview Assets.xcassets/Contents.json | 6 + Samples/Swift/AppAttestExample/Podfile | 12 + Samples/Swift/AppAttestExample/README.md | 0 33 files changed, 1968 insertions(+), 63 deletions(-) create mode 100644 GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h create mode 100644 GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h create mode 100644 GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m create mode 100644 GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h create mode 100644 GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m create mode 100644 GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h create mode 100644 GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h create mode 100644 GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h create mode 100644 GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.m create mode 100644 GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h create mode 100644 GoogleSignIn/Tests/Unit/GIDAppCheckTest.m create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExample.entitlements create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/BirthdayAppCheckProviderFactory.swift create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/BirthdayLoader.swift create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/ContentView.swift create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/AccentColor.colorset/Contents.json create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/AppIcon.appiconset/Contents.json create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/Contents.json create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Samples/Swift/AppAttestExample/Podfile create mode 100644 Samples/Swift/AppAttestExample/README.md diff --git a/.gitignore b/.gitignore index 166e2919..538419c3 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ Credentials.xcconfig Pods/ gen/ Podfile.lock + +# Firebase App Check Example +**/GoogleService-Info.plist diff --git a/GoogleSignIn.podspec b/GoogleSignIn.podspec index c6d7c612..9b9a26bd 100644 --- a/GoogleSignIn.podspec +++ b/GoogleSignIn.podspec @@ -13,7 +13,7 @@ The Google Sign-In SDK allows users to sign in with their Google account from th :tag => s.version.to_s } s.swift_version = '4.0' - ios_deployment_target = '10.0' + ios_deployment_target = '11.0' osx_deployment_target = '10.15' s.ios.deployment_target = ios_deployment_target s.osx.deployment_target = osx_deployment_target @@ -33,6 +33,7 @@ The Google Sign-In SDK allows users to sign in with their Google account from th ] s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' + s.dependency 'FirebaseAppCheck', '~> 10.0' s.dependency 'AppAuth', '>= 1.7.3', '< 2.0' s.dependency 'GTMAppAuth', '>= 4.1.1', '< 5.0' s.dependency 'GTMSessionFetcher/Core', '~> 3.3' diff --git a/GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h b/GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h new file mode 100644 index 00000000..7f934ec7 --- /dev/null +++ b/GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +@protocol GIDAppCheckTokenFetcher; +@class FIRAppCheckToken; + +/// Interface providing the API for both pre-warming `GIDSignIn` to use Firebase App Check and +/// fetching the App Check token. +NS_AVAILABLE_IOS(14) +@protocol GIDAppCheckProvider + +/// Creates the instance of this App Check wrapper class. +/// +/// @param tokenFetcher The instance performing the Firebase App Check token requests. If `provider` +/// is nil, then we default to `FIRAppCheck`. +/// @param userDefaults The instance of `NSUserDefaults` that `GIDAppCheck` will use to store its +/// preparation status. If nil, `GIDAppCheck` will use `-[NSUserDefaults standardUserDefaults]`. +- (instancetype)initWithAppCheckTokenFetcher:(nullable id)tokenFetcher + userDefaults:(nullable NSUserDefaults *)userDefaults; + +/// Prewarms the library for App Check by asking Firebase App Check to generate the App Attest key +/// id and perform the initial attestation process (if needed). +/// +/// @param completion A `nullable` callback with a `nullable` `NSError` if preparation fails. +- (void)prepareForAppCheckWithCompletion:(nullable void (^)(NSError * _Nullable error))completion; + +/// Fetches the limited use Firebase token. +/// +/// @param completion A `nullable` callback with the `FIRAppCheckToken` if present, or an `NSError` +/// otherwise. +- (void)getLimitedUseTokenWithCompletion:(nullable void (^)(FIRAppCheckToken * _Nullable token, + NSError * _Nullable error))completion; + +/// Whether or not the App Attest key ID created and the attestation object has been fetched. +- (BOOL)isPrepared; + +@end + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h new file mode 100644 index 00000000..96d63ea2 --- /dev/null +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import +#import "GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h" + +NS_ASSUME_NONNULL_BEGIN + +@class FIRAppCheckToken; +extern NSString *const kGIDAppCheckPreparedKey; + +NS_CLASS_AVAILABLE_IOS(14) +@interface GIDAppCheck : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m new file mode 100644 index 00000000..1b7a8095 --- /dev/null +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m @@ -0,0 +1,135 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" +#import "GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h" +#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h" + +@import FirebaseAppCheck; + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +NSErrorDomain const kGIDAppCheckErrorDomain = @"com.google.GIDAppCheck"; +NSString *const kGIDAppCheckPreparedKey = @"com.google.GIDAppCheckPreparedKey"; + +typedef void (^GIDAppCheckPrepareCompletion)(NSError * _Nullable); +typedef void (^GIDAppCheckTokenCompletion)(FIRAppCheckToken * _Nullable, NSError * _Nullable); + +@interface GIDAppCheck () + +@property(nonatomic, strong) id tokenFetcher; +@property(nonatomic, strong) dispatch_queue_t workerQueue; +@property(nonatomic, strong) NSUserDefaults *userDefaults; +@property(atomic, strong) NSMutableArray *prepareCompletions; +@property(atomic) BOOL preparing; + +@end + +@implementation GIDAppCheck + +- (instancetype)initWithAppCheckTokenFetcher:(nullable id)tokenFetcher + userDefaults:(nullable NSUserDefaults *)userDefaults { + if (self = [super init]) { + _tokenFetcher = tokenFetcher ?: [FIRAppCheck appCheck]; + _userDefaults = userDefaults ?: [NSUserDefaults standardUserDefaults]; + _workerQueue = dispatch_queue_create("com.google.googlesignin.GIDAppCheckWorkerQueue", nil); + _prepareCompletions = [NSMutableArray array]; + _preparing = NO; + } + return self; +} + +- (BOOL)isPrepared { + return [self.userDefaults boolForKey:kGIDAppCheckPreparedKey]; +} + +- (void)prepareForAppCheckWithCompletion:(nullable GIDAppCheckPrepareCompletion)completion { + if (completion) { + @synchronized (self) { + [self.prepareCompletions addObject:completion]; + } + } + + @synchronized (self) { + if (self.preparing) { + return; + } + + self.preparing = YES; + } + + dispatch_async(self.workerQueue, ^{ + NSArray * __block callbacks; + + if ([self isPrepared]) { + NSArray *callbacks; + @synchronized (self) { + callbacks = [self.prepareCompletions copy]; + [self.prepareCompletions removeAllObjects]; + self.preparing = NO; + } + + for (GIDAppCheckPrepareCompletion savedCompletion in callbacks) { + savedCompletion(nil); + } + return; + } + + [self.tokenFetcher limitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, + NSError * _Nullable error) { + NSError * __block maybeError = error; + @synchronized (self) { + if (!token && !error) { + maybeError = [NSError errorWithDomain:kGIDAppCheckErrorDomain + code:kGIDAppCheckUnexpectedError + userInfo:nil]; + } + + if (token) { + [self.userDefaults setBool:YES forKey:kGIDAppCheckPreparedKey]; + } + + callbacks = [self.prepareCompletions copy]; + [self.prepareCompletions removeAllObjects]; + self.preparing = NO; + } + + + for (GIDAppCheckPrepareCompletion savedCompletion in callbacks) { + savedCompletion(maybeError); + } + }]; + }); +} + +- (void)getLimitedUseTokenWithCompletion:(nullable GIDAppCheckTokenCompletion)completion { + dispatch_async(self.workerQueue, ^{ + [self.tokenFetcher limitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, + NSError * _Nullable error) { + if (token) { + [self.userDefaults setBool:YES forKey:kGIDAppCheckPreparedKey]; + } + if (completion) { + completion(token, error); + } + }]; + }); +} + +@end + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h new file mode 100644 index 00000000..f0c77be9 --- /dev/null +++ b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import + +NS_ASSUME_NONNULL_BEGIN + +NS_CLASS_AVAILABLE_IOS(14.0) +/// A `UIViewController` presented onscreen to indicate to the user that GSI is performing blocking +/// work. +@interface GIDActivityIndicatorViewController : UIViewController + +/// The indicator view spinning on screen. +@property(nonatomic, strong, readonly) UIActivityIndicatorView *activityIndicator; + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m new file mode 100644 index 00000000..c5615ac2 --- /dev/null +++ b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GIDActivityIndicatorViewController.h" + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import + +@implementation GIDActivityIndicatorViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge]; + self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO; + [self.activityIndicator startAnimating]; + [self.view addSubview:self.activityIndicator]; + + NSLayoutConstraint *centerX = + [self.activityIndicator.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor]; + NSLayoutConstraint *centerY = + [self.activityIndicator.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor]; + [NSLayoutConstraint activateConstraints:@[centerX, centerY]]; +} + +@end + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h new file mode 100644 index 00000000..57af940e --- /dev/null +++ b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +NS_ASSUME_NONNULL_BEGIN + +@class FIRAppCheckToken; + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +NS_AVAILABLE_IOS(14) +@protocol GIDAppCheckTokenFetcher + +/// Get the limited use `FIRAppCheckToken`. +/// +/// @param completion A block that passes back the `FIRAppCheckToken` upon success or an error in +/// the case of any failure. +- (void)limitedUseTokenWithCompletion:(nullable void (^)(FIRAppCheckToken * _Nullable token, + NSError * _Nullable error))completion; + +@end + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h new file mode 100644 index 00000000..0ec82aeb --- /dev/null +++ b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import FirebaseAppCheck; +#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h" + +NS_ASSUME_NONNULL_BEGIN + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +@interface FIRAppCheck (FIRAppCheck_GIDAppCheckTokenFetcher) +@end + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h new file mode 100644 index 00000000..ad994a92 --- /dev/null +++ b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h @@ -0,0 +1,45 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h" + +@class FIRAppCheckToken; + +NS_ASSUME_NONNULL_BEGIN + +extern NSUInteger const kGIDAppCheckTokenFetcherTokenError; + +NS_CLASS_AVAILABLE_IOS(14) +@interface GIDAppCheckTokenFetcherFake : NSObject + +/// Creates an instance with the provided app check token and error. +/// +/// This protocol is mainly used for testing purposes so that the token fetching from Firebase App +/// Check can be faked. +/// @param token The `FIRAppCheckToken` to pass into the completion called from +/// `limitedUseTokenWithCompletion:`. +/// @param error The `NSError` to pass into the completion called from +/// `limitedUseTokenWithCompletion:`. +- (instancetype)initWithAppCheckToken:(nullable FIRAppCheckToken *)token + error:(nullable NSError *)error; + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.m b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.m new file mode 100644 index 00000000..20719a9e --- /dev/null +++ b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.m @@ -0,0 +1,51 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h" + +@import FirebaseAppCheck; + +NSUInteger const kGIDAppCheckTokenFetcherTokenError = 1; + +@interface GIDAppCheckTokenFetcherFake () + +@property(nonatomic, strong, nullable) FIRAppCheckToken *token; +@property(nonatomic, strong, nullable) NSError *error; + +@end + +@implementation GIDAppCheckTokenFetcherFake + +- (instancetype)initWithAppCheckToken:(nullable FIRAppCheckToken *)token + error:(nullable NSError *)error { + if (self = [super init]) { + _token = token; + _error = error; + } + return self; +} + +- (void)limitedUseTokenWithCompletion:(void (^)(FIRAppCheckToken * _Nullable, + NSError * _Nullable))completion { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(self.token, self.error); + }); +} + +@end + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDGoogleUser.m b/GoogleSignIn/Sources/GIDGoogleUser.m index ec300839..817f57e1 100644 --- a/GoogleSignIn/Sources/GIDGoogleUser.m +++ b/GoogleSignIn/Sources/GIDGoogleUser.m @@ -117,6 +117,7 @@ - (GIDConfiguration *)configuration { return _cachedConfiguration; } +// TODO: Should the refresh tokens flow also use App Check? (mdmathias, 2023.05.23) - (void)refreshTokensIfNeededWithCompletion:(GIDGoogleUserCompletion)completion { if (!([self.accessToken.expirationDate timeIntervalSinceNow] < kMinimalTimeToExpire || (self.idToken && [self.idToken.expirationDate timeIntervalSinceNow] < kMinimalTimeToExpire))) { diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index 4b942ed9..25080d1c 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -28,6 +28,10 @@ #import "GoogleSignIn/Sources/GIDScopes.h" #import "GoogleSignIn/Sources/GIDSignInCallbackSchemes.h" #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#import "FirebaseAppCheck/FIRAppCheckToken.h" +#import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" +#import "GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h" #import "GoogleSignIn/Sources/GIDAuthStateMigration.h" #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h" #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST @@ -132,6 +136,12 @@ static NSString *const kLoginHintParameter = @"login_hint"; static NSString *const kHostedDomainParameter = @"hd"; +// Parameters for auth and token exchange endpoints using App Attest. +static NSString *const kClientAssertionParameter = @"client_assertion"; +static NSString *const kClientAssertionTypeParameter = @"client_assertion_type"; +static NSString *const kClientAssertionTypeParameterValue = + @"urn:ietf:params:oauth:client-assertion-type:appcheck"; + // Minimum time to expiration for a restored access token. static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0; @@ -159,6 +169,9 @@ @implementation GIDSignIn { // set when a sign-in flow is begun via |signInWithOptions:| when the options passed don't // represent a sign in continuation. GIDSignInInternalOptions *_currentOptions; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + id _appCheck API_AVAILABLE(ios(14)); +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST // AppAuth configuration object. OIDServiceConfiguration *_appAuthConfiguration; // AppAuth external user-agent session state. @@ -443,11 +456,37 @@ + (GIDSignIn *)sharedInstance { static dispatch_once_t once; static GIDSignIn *sharedInstance; dispatch_once(&once, ^{ - sharedInstance = [[self alloc] initPrivate]; + GTMKeychainStore *keychainStore = + [[GTMKeychainStore alloc] initWithItemName:kGTMAppAuthKeychainName]; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + if (@available(iOS 14.0, *)) { + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:nil + userDefaults:nil]; + sharedInstance = [[self alloc] initWithKeychainStore:keychainStore + appCheckProvider:appCheck]; + } +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + if (!sharedInstance) { + sharedInstance = [[self alloc] initWithKeychainStore:keychainStore]; + } }); return sharedInstance; } +#pragma mark - Configuring and pre-warming + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +- (void)configureWithCompletion:(nullable void (^)(NSError * _Nullable))completion { + @synchronized(self) { + [_appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { + if (completion) { + completion(error); + } + }]; + } +} +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + #pragma mark - Private methods - (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore { @@ -470,14 +509,13 @@ - (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore { } NSString *authorizationEnpointURL = [NSString stringWithFormat:kAuthorizationURLTemplate, - [GIDSignInPreferences googleAuthorizationServer]]; + [GIDSignInPreferences googleAuthorizationServer]]; NSString *tokenEndpointURL = [NSString stringWithFormat:kTokenURLTemplate, - [GIDSignInPreferences googleTokenServer]]; + [GIDSignInPreferences googleTokenServer]]; _appAuthConfiguration = [[OIDServiceConfiguration alloc] - initWithAuthorizationEndpoint:[NSURL URLWithString:authorizationEnpointURL] - tokenEndpoint:[NSURL URLWithString:tokenEndpointURL]]; + initWithAuthorizationEndpoint:[NSURL URLWithString:authorizationEnpointURL] + tokenEndpoint:[NSURL URLWithString:tokenEndpointURL]]; _keychainStore = keychainStore; - #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST // Perform migration of auth state from old (before 5.0) versions of the SDK if needed. GIDAuthStateMigration *migration = @@ -491,11 +529,16 @@ - (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore { return self; } -- (instancetype)initPrivate { - GTMKeychainStore *keychainStore = - [[GTMKeychainStore alloc] initWithItemName:kGTMAppAuthKeychainName]; - return [self initWithKeychainStore:keychainStore]; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +- (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore + appCheckProvider:(id)appCheckProvider { + self = [self initWithKeychainStore:keychainStore]; + if (self) { + _appCheck = appCheckProvider; + } + return self; } +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST // Does sanity check for parameters and then authenticates if necessary. - (void)signInWithOptions:(GIDSignInInternalOptions *)options { @@ -557,11 +600,6 @@ - (void)signInWithOptions:(GIDSignInInternalOptions *)options { #pragma mark - Authentication flow - (void)authenticateInteractivelyWithOptions:(GIDSignInInternalOptions *)options { - GIDSignInCallbackSchemes *schemes = - [[GIDSignInCallbackSchemes alloc] initWithClientIdentifier:options.configuration.clientID]; - NSURL *redirectURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@:%@", - [schemes clientIdentifierScheme], - kBrowserCallbackPath]]; NSString *emmSupport; #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST emmSupport = [[self class] isOperatingSystemAtLeast9] ? kEMMVersion : nil; @@ -569,7 +607,100 @@ - (void)authenticateInteractivelyWithOptions:(GIDSignInInternalOptions *)options emmSupport = nil; #endif // TARGET_OS_MACCATALYST || TARGET_OS_OSX - NSMutableDictionary *additionalParameters = [@{} mutableCopy]; + [self authorizationRequestWithOptions:options + completion:^(OIDAuthorizationRequest * _Nullable request, + NSError * _Nullable error) { + self->_currentAuthorizationFlow = + [OIDAuthorizationService presentAuthorizationRequest:request +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + presentingViewController:options.presentingViewController +#elif TARGET_OS_OSX + presentingWindow:options.presentingWindow +#endif // TARGET_OS_OSX + callback: + ^(OIDAuthorizationResponse *_Nullable authorizationResponse, + NSError *_Nullable error) { + [self processAuthorizationResponse:authorizationResponse + error:error + emmSupport:emmSupport]; + }]; + }]; +} + +- (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options completion: + (void (^)(OIDAuthorizationRequest *_Nullable request, NSError *_Nullable error))completion { + BOOL shouldCallCompletion = YES; + NSMutableDictionary *additionalParameters = + [self additionalParametersFromOptions:options]; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + if (@available(iOS 14.0, *)) { + if (_appCheck) { + shouldCallCompletion = NO; + GIDActivityIndicatorViewController *activityVC = + [[GIDActivityIndicatorViewController alloc] init]; + [options.presentingViewController presentViewController:activityVC + animated:true + completion:^{ + // Ensure that the activity indicator shows for at least 1/2 second to prevent "flashing" + // TODO: Re-implement per: https://github.com/google/GoogleSignIn-iOS/issues/329 + dispatch_time_t halfSecond = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC / 2); + dispatch_after(halfSecond, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self->_appCheck getLimitedUseTokenWithCompletion: + ^(FIRAppCheckToken * _Nullable token, NSError * _Nullable error) { + if (token) { + additionalParameters[kClientAssertionTypeParameter] = + kClientAssertionTypeParameterValue; + additionalParameters[kClientAssertionParameter] = token.token; + OIDAuthorizationRequest *request = + [self authorizationRequestWithOptions:options + additionalParameters:additionalParameters]; + [activityVC.activityIndicator stopAnimating]; + [activityVC dismissViewControllerAnimated:YES completion:nil]; + completion(request, nil); + return; + } + [activityVC.activityIndicator stopAnimating]; + [activityVC dismissViewControllerAnimated:YES completion:nil]; + completion(nil, error); + return; + }]; + }); + }]; + } + } +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + if (shouldCallCompletion) { + OIDAuthorizationRequest *request = [self authorizationRequestWithOptions:options + additionalParameters:additionalParameters]; + completion(request, nil); + } +} + + +- (OIDAuthorizationRequest *) + authorizationRequestWithOptions:(GIDSignInInternalOptions *)options + additionalParameters:(NSDictionary *)additionalParameters { + OIDAuthorizationRequest *request = + [[OIDAuthorizationRequest alloc] initWithConfiguration:_appAuthConfiguration + clientId:options.configuration.clientID + scopes:options.scopes + redirectURL:[self redirectURLWithOptions:options] + responseType:OIDResponseTypeCode + additionalParameters:additionalParameters]; + return request; +} + +- (NSMutableDictionary *) + additionalParametersFromOptions:(GIDSignInInternalOptions *)options { + NSString *emmSupport; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + emmSupport = [[self class] isOperatingSystemAtLeast9] ? kEMMVersion : nil; +#elif TARGET_OS_MACCATALYST || TARGET_OS_OSX + emmSupport = nil; +#endif // TARGET_OS_MACCATALYST || TARGET_OS_OSX + + NSMutableDictionary *additionalParameters = + [[NSMutableDictionary alloc] init]; additionalParameters[kIncludeGrantedScopesParameter] = @"true"; if (options.configuration.serverClientID) { additionalParameters[kAudienceParameter] = options.configuration.serverClientID; @@ -592,27 +723,16 @@ - (void)authenticateInteractivelyWithOptions:(GIDSignInInternalOptions *)options additionalParameters[kSDKVersionLoggingParameter] = GIDVersion(); additionalParameters[kEnvironmentLoggingParameter] = GIDEnvironment(); - OIDAuthorizationRequest *request = - [[OIDAuthorizationRequest alloc] initWithConfiguration:_appAuthConfiguration - clientId:options.configuration.clientID - scopes:options.scopes - redirectURL:redirectURL - responseType:OIDResponseTypeCode - additionalParameters:additionalParameters]; + return additionalParameters; +} - _currentAuthorizationFlow = [OIDAuthorizationService - presentAuthorizationRequest:request -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - presentingViewController:options.presentingViewController -#elif TARGET_OS_OSX - presentingWindow:options.presentingWindow -#endif // TARGET_OS_OSX - callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, - NSError *_Nullable error) { - [self processAuthorizationResponse:authorizationResponse - error:error - emmSupport:emmSupport]; - }]; +- (NSURL *)redirectURLWithOptions:(GIDSignInInternalOptions *)options { + GIDSignInCallbackSchemes *schemes = + [[GIDSignInCallbackSchemes alloc] initWithClientIdentifier:options.configuration.clientID]; + NSURL *redirectURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@:%@", + [schemes clientIdentifierScheme], + kBrowserCallbackPath]]; + return redirectURL; } - (void)processAuthorizationResponse:(OIDAuthorizationResponse *)authorizationResponse @@ -677,7 +797,6 @@ - (void)processAuthorizationResponse:(OIDAuthorizationResponse *)authorizationRe // Perform authentication with the provided options. - (void)authenticateWithOptions:(GIDSignInInternalOptions *)options { - // If this is an interactive flow, we're not going to try to restore any saved auth state. if (options.interactive) { [self authenticateInteractivelyWithOptions:options]; diff --git a/GoogleSignIn/Sources/GIDSignIn_Private.h b/GoogleSignIn/Sources/GIDSignIn_Private.h index 2fb71ae4..7210ecd8 100644 --- a/GoogleSignIn/Sources/GIDSignIn_Private.h +++ b/GoogleSignIn/Sources/GIDSignIn_Private.h @@ -29,6 +29,7 @@ NS_ASSUME_NONNULL_BEGIN @class GIDGoogleUser; @class GIDSignInInternalOptions; @class GTMKeychainStore; +@protocol GIDAppCheckProvider; /// Represents a completion block that takes a `GIDSignInResult` on success or an error if the /// operation was unsuccessful. @@ -44,12 +45,16 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error); /// Redeclare |currentUser| as readwrite for internal use. @property(nonatomic, readwrite, nullable) GIDGoogleUser *currentUser; -/// Private initializer for |GIDSignIn|. -- (instancetype)initPrivate; - -/// Private initializer taking a `GTMKeychainStore` to use during tests. +/// Private initializer taking a `GTMKeychainStore`. - (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +/// Private initializer taking a `GTMKeychainStore` and `GIDAppCheckProvider`. +- (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore + appCheckProvider:(id)appCheckProvider +API_AVAILABLE(ios(14)); +#endif // TARGET_OS_IOS || !TARGET_OS_MACCATALYST + /// Authenticates with extra options. - (void)signInWithOptions:(GIDSignInInternalOptions *)options; diff --git a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h new file mode 100644 index 00000000..cd443e40 --- /dev/null +++ b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// The error domain for `NSError`s returned by the Google Sign-In SDK related to App Check. +extern NSErrorDomain const kGIDAppCheckErrorDomain; + +/// A list of potential error codes returned from the Google Sign-In SDK during App Check. +typedef NS_ERROR_ENUM(kGIDAppCheckErrorDomain, GIDAppCheckErrorCode) { + /// An unexpected error was encountered. + kGIDAppCheckUnexpectedError = 1, +}; +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h index 2576b13d..c792c8ac 100644 --- a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h +++ b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h @@ -26,6 +26,7 @@ @class GIDConfiguration; @class GIDGoogleUser; @class GIDSignInResult; +@protocol GIDAppCheckProvider; NS_ASSUME_NONNULL_BEGIN @@ -66,6 +67,22 @@ typedef NS_ERROR_ENUM(kGIDSignInErrorDomain, GIDSignInErrorCode) { /// The active configuration for this instance of `GIDSignIn`. @property(nonatomic, nullable) GIDConfiguration *configuration; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +/// Configures `GIDSignIn` for use. +/// +/// @param completion A nullable callback block passing back any error arising from the +/// configuration process if any exists. +/// +/// Call this method on `GIDSignIn` prior to use and as early as possible. This method generates App +/// Attest key IDs and the attestation object eagerly to minimize latency later on during the sign +/// in or add scopes flows. +- (void)configureWithCompletion:(nullable void (^)(NSError * _Nullable error))completion +API_AVAILABLE(ios(14)) +NS_SWIFT_NAME(configureWithCompletion(completion:)); + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + /// Unavailable. Use the `sharedInstance` property to instantiate `GIDSignIn`. /// :nodoc: + (instancetype)new NS_UNAVAILABLE; @@ -141,9 +158,9 @@ typedef NS_ERROR_ENUM(kGIDSignInErrorDomain, GIDSignInErrorCode) { - (void)signInWithPresentingViewController:(UIViewController *)presentingViewController hint:(nullable NSString *)hint completion: - (nullable void (^)(GIDSignInResult *_Nullable signInResult, - NSError *_Nullable error))completion - NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions."); +(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion +NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions."); /// Starts an interactive sign-in flow on iOS using the provided hint and additional scopes. /// @@ -163,9 +180,9 @@ typedef NS_ERROR_ENUM(kGIDSignInErrorDomain, GIDSignInErrorCode) { hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes completion: - (nullable void (^)(GIDSignInResult *_Nullable signInResult, - NSError *_Nullable error))completion - NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions."); +(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion +NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions."); #elif TARGET_OS_OSX diff --git a/GoogleSignIn/Sources/Public/GoogleSignIn/GoogleSignIn.h b/GoogleSignIn/Sources/Public/GoogleSignIn/GoogleSignIn.h index 5923329d..4fd17ede 100644 --- a/GoogleSignIn/Sources/Public/GoogleSignIn/GoogleSignIn.h +++ b/GoogleSignIn/Sources/Public/GoogleSignIn/GoogleSignIn.h @@ -15,6 +15,9 @@ */ #import +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#import "GIDAppCheckError.h" +#endif #import "GIDConfiguration.h" #import "GIDGoogleUser.h" #import "GIDProfileData.h" diff --git a/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m b/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m new file mode 100644 index 00000000..f4e49d26 --- /dev/null +++ b/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m @@ -0,0 +1,185 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import +#import "FirebaseAppCheck/FIRAppCheckToken.h" +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" +#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h" + +static NSUInteger const timeout = 1; +static NSString *const kUserDefaultsSuiteName = @"GIDAppCheckKeySuiteName"; + +NS_CLASS_AVAILABLE_IOS(14) +@interface GIDAppCheckTest : XCTestCase + +@property(nonatomic, strong) NSUserDefaults *userDefaults; + +@end + +@implementation GIDAppCheckTest + +- (void)setUp { + [super setUp]; + _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:kUserDefaultsSuiteName]; +} + +- (void)tearDown { + [super tearDown]; + [self.userDefaults removeObjectForKey:kGIDAppCheckPreparedKey]; + [self.userDefaults removeSuiteNamed:kUserDefaultsSuiteName]; +} + +- (void)testGetLimitedUseTokenFailure { + XCTestExpectation *tokenFailExpectation = + [self expectationWithDescription:@"App check token fail"]; + NSError *expectedError = [NSError errorWithDomain:kGIDAppCheckErrorDomain + code:kGIDAppCheckTokenFetcherTokenError + userInfo:nil]; + GIDAppCheckTokenFetcherFake *tokenFetcher = + [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:nil error:expectedError]; + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher + userDefaults:self.userDefaults]; + + [appCheck getLimitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, + NSError * _Nullable error) { + XCTAssertNil(token); + XCTAssertEqualObjects(expectedError, error); + [tokenFailExpectation fulfill]; + }]; + + [self waitForExpectations:@[tokenFailExpectation] timeout:timeout]; +} + +- (void)testIsPreparedError { + XCTestExpectation *notAlreadyPreparedExpectation = + [self expectationWithDescription:@"App check not already prepared error"]; + + FIRAppCheckToken *expectedToken = [[FIRAppCheckToken alloc] initWithToken:@"foo" + expirationDate:[NSDate distantFuture]]; + // It doesn't matter what we pass for the error since we will check `isPrepared` and make one + GIDAppCheckTokenFetcherFake *tokenFetcher = + [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:expectedToken error:nil]; + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher + userDefaults:self.userDefaults]; + + [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { + XCTAssertNil(error); + [notAlreadyPreparedExpectation fulfill]; + }]; + + [self waitForExpectations:@[notAlreadyPreparedExpectation] timeout:timeout]; + + XCTestExpectation *alreadyPreparedExpectation = + [self expectationWithDescription:@"App check already prepared error"]; + + [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { + XCTAssertNil(error); + [alreadyPreparedExpectation fulfill]; + }]; + + [self waitForExpectations:@[alreadyPreparedExpectation] timeout:timeout]; + + XCTAssertTrue([appCheck isPrepared]); +} + +- (void)testGetLimitedUseTokenSucceeds { + XCTestExpectation *prepareExpectation = + [self expectationWithDescription:@"Prepare for App Check expectation"]; + FIRAppCheckToken *expectedToken = [[FIRAppCheckToken alloc] initWithToken:@"foo" + expirationDate:[NSDate distantFuture]]; + + GIDAppCheckTokenFetcherFake *tokenFetcher = + [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:expectedToken error:nil]; + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher + userDefaults:self.userDefaults]; + + [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { + XCTAssertNil(error); + [prepareExpectation fulfill]; + }]; + + // Wait for preparation to complete to test `isPrepared` + [self waitForExpectations:@[prepareExpectation] timeout:timeout]; + + XCTAssertTrue(appCheck.isPrepared); + + XCTestExpectation *getLimitedUseTokenSucceedsExpectation = + [self expectationWithDescription:@"getLimitedUseToken should succeed"]; + + [appCheck getLimitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, + NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertNotNil(token); + XCTAssertEqualObjects(token, expectedToken); + [getLimitedUseTokenSucceedsExpectation fulfill]; + }]; + + [self waitForExpectations:@[getLimitedUseTokenSucceedsExpectation] timeout:timeout]; + + XCTAssertTrue([appCheck isPrepared]); +} + +- (void)testAsyncCompletions { + XCTestExpectation *firstPrepareExpectation = + [self expectationWithDescription:@"First async prepare for App Check expectation"]; + + XCTestExpectation *secondPrepareExpectation = + [self expectationWithDescription:@"Second async prepare for App Check expectation"]; + + FIRAppCheckToken *expectedToken = [[FIRAppCheckToken alloc] initWithToken:@"foo" + expirationDate:[NSDate distantFuture]]; + + GIDAppCheckTokenFetcherFake *tokenFetcher = + [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:expectedToken error:nil]; + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher + userDefaults:self.userDefaults]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { + XCTAssertNil(error); + [firstPrepareExpectation fulfill]; + }]; + }); + + dispatch_async(dispatch_get_main_queue(), ^{ + [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { + XCTAssertNil(error); + [secondPrepareExpectation fulfill]; + }]; + }); + + [self waitForExpectations:@[firstPrepareExpectation, secondPrepareExpectation] timeout:timeout]; + + XCTAssertTrue([appCheck isPrepared]); + + // Simulate requesting later on after `appCheck` is prepared + XCTestExpectation *preparedExpectation = + [self expectationWithDescription:@"Prepared expectation"]; + + [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { + XCTAssertNil(error); + [preparedExpectation fulfill]; + }]; + + [self waitForExpectations:@[preparedExpectation] timeout:timeout]; +} + +@end + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index c64f1611..a0777ddb 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -34,6 +34,9 @@ #import "GoogleSignIn/Sources/GIDSignInPreferences.h" #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#import "FirebaseAppCheck/FIRAppCheckToken.h" +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" +#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h" #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h" #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST @@ -79,6 +82,9 @@ return YES;\ }] +/// `NSUserDefaults` suite name for testing with `GIDAppCheck`. +static NSString *const kUserDefaultsSuiteName = @"GIDAppCheckKeySuiteName"; + static NSString * const kFakeGaiaID = @"123456789"; static NSString * const kFakeIDToken = @"FakeIDToken"; static NSString * const kClientId = @"FakeClientID"; @@ -261,6 +267,11 @@ @interface GIDSignInTest : XCTestCase { // Status returned by saveAuthorization:toKeychainForName: BOOL _saveAuthorizationReturnValue; + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + // Test userDefaults for use with `GIDAppCheck` + NSUserDefaults *_testUserDefaults; +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST } @end @@ -325,12 +336,15 @@ - (void)setUp { [_fakeMainBundle fakeAllSchemesSupported]; // Object under test - [[NSUserDefaults standardUserDefaults] setBool:YES - forKey:kAppHasRunBeforeKey]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAppHasRunBeforeKey]; _signIn = [[GIDSignIn alloc] initWithKeychainStore:_keychainStore]; _hint = nil; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + _testUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:kUserDefaultsSuiteName]; +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + __weak GIDSignInTest *weakSelf = self; _completion = ^(GIDSignInResult *_Nullable signInResult, NSError * _Nullable error) { GIDSignInTest *strongSelf = weakSelf; @@ -357,6 +371,11 @@ - (void)tearDown { OCMVerifyAll(_presentingWindow); #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST + [[NSUserDefaults standardUserDefaults] removeObjectForKey:kAppHasRunBeforeKey]; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + [_testUserDefaults removeObjectForKey:kGIDAppCheckPreparedKey]; + [_testUserDefaults removeSuiteNamed:kUserDefaultsSuiteName]; +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST [_fakeMainBundle stopFaking]; [super tearDown]; @@ -364,14 +383,63 @@ - (void)tearDown { #pragma mark - Tests -- (void)testShareInstance { - GIDSignIn *signIn1 = GIDSignIn.sharedInstance; - GIDSignIn *signIn2 = GIDSignIn.sharedInstance; - XCTAssertTrue(signIn1 == signIn2, @"shared instance must be singleton"); +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +- (void)testConfigureSucceeds { + if (@available(iOS 14, *)) { + XCTestExpectation *configureSucceedsExpecation = + [self expectationWithDescription:@"Configure succeeds expectation"]; + + FIRAppCheckToken *token = [[FIRAppCheckToken alloc] initWithToken:@"foo" + expirationDate:[NSDate distantFuture]]; + GIDAppCheckTokenFetcherFake *tokenFetcher = + [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:token error:nil]; + GIDAppCheck *appCheckProvider = + [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher + userDefaults:_testUserDefaults]; + + GIDSignIn *signIn = [[GIDSignIn alloc] initWithKeychainStore:_keychainStore + appCheckProvider:appCheckProvider]; + [signIn configureWithCompletion:^(NSError * _Nullable error) { + XCTAssertNil(error); + [configureSucceedsExpecation fulfill]; + }]; + + [self waitForExpectations:@[configureSucceedsExpecation] timeout:1]; + XCTAssertTrue(appCheckProvider.isPrepared); + } +} + +- (void)testConfigureFailsNoTokenOrError { + if (@available(iOS 14, *)) { + XCTestExpectation *configureFailsExpecation = + [self expectationWithDescription:@"Configure fails expectation"]; + + GIDAppCheckTokenFetcherFake *tokenFetcher = + [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:nil error:nil]; + GIDAppCheck *appCheckProvider = + [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher + userDefaults:_testUserDefaults]; + + GIDSignIn *signIn = [[GIDSignIn alloc] initWithKeychainStore:_keychainStore + appCheckProvider:appCheckProvider]; + + // `configureWithCompletion:` should fail if neither a token or error is present + [signIn configureWithCompletion:^(NSError * _Nullable error) { + XCTAssertNotNil(error); + XCTAssertEqual(error.code, kGIDAppCheckUnexpectedError); + [configureFailsExpecation fulfill]; + }]; + + [self waitForExpectations:@[configureFailsExpecation] timeout:1]; + XCTAssertFalse(appCheckProvider.isPrepared); + } } +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST -- (void)testInitPrivate { - GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate]; +- (void)testInitWithKeychainStore { + GTMKeychainStore *store = [[GTMKeychainStore alloc] initWithItemName:@"foo"]; + GIDSignIn *signIn; + signIn = [[GIDSignIn alloc] initWithKeychainStore:store]; XCTAssertNotNil(signIn.configuration); XCTAssertEqual(signIn.configuration.clientID, kClientId); XCTAssertNil(signIn.configuration.serverClientID); @@ -379,21 +447,26 @@ - (void)testInitPrivate { XCTAssertNil(signIn.configuration.openIDRealm); } -- (void)testInitPrivate_noConfig { +- (void)testInitWithKeychainStore_noConfig { [_fakeMainBundle fakeWithClientID:nil serverClientID:nil hostedDomain:nil openIDRealm:nil]; - GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate]; + GTMKeychainStore *store = [[GTMKeychainStore alloc] initWithItemName:@"foo"]; + GIDSignIn *signIn; + signIn = [[GIDSignIn alloc] initWithKeychainStore:store]; XCTAssertNil(signIn.configuration); } -- (void)testInitPrivate_fullConfig { +- (void)testInitWithKeychainStore_fullConfig { [_fakeMainBundle fakeWithClientID:kClientId serverClientID:kServerClientId hostedDomain:kFakeHostedDomain openIDRealm:kOpenIDRealm]; - GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate]; + + GTMKeychainStore *store = [[GTMKeychainStore alloc] initWithItemName:@"foo"]; + GIDSignIn *signIn; + signIn = [[GIDSignIn alloc] initWithKeychainStore:store]; XCTAssertNotNil(signIn.configuration); XCTAssertEqual(signIn.configuration.clientID, kClientId); XCTAssertEqual(signIn.configuration.serverClientID, kServerClientId); @@ -401,12 +474,14 @@ - (void)testInitPrivate_fullConfig { XCTAssertEqual(signIn.configuration.openIDRealm, kOpenIDRealm); } -- (void)testInitPrivate_invalidConfig { +- (void)testInitWithKeychainStore_invalidConfig { [_fakeMainBundle fakeWithClientID:@[ @"bad", @"config", @"values" ] serverClientID:nil hostedDomain:nil openIDRealm:nil]; - GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate]; + GTMKeychainStore *store = [[GTMKeychainStore alloc] initWithItemName:@"foo"]; + GIDSignIn *signIn; + signIn = [[GIDSignIn alloc] initWithKeychainStore:store]; XCTAssertNil(signIn.configuration); } diff --git a/Package.swift b/Package.swift index 8336f199..03a7d466 100644 --- a/Package.swift +++ b/Package.swift @@ -24,7 +24,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .macOS(.v10_15), - .iOS(.v10) + .iOS(.v11) ], products: [ .library( @@ -45,6 +45,10 @@ let package = Package( name: "AppAuth", url: "https://github.com/openid/AppAuth-iOS.git", from: "1.7.3"), + .package( + name: "Firebase", + url: "https://github.com/firebase/firebase-ios-sdk.git", + "10.0.0" ..< "11.0.0"), .package( name: "GTMAppAuth", url: "https://github.com/google/GTMAppAuth.git", @@ -67,6 +71,7 @@ let package = Package( name: "GoogleSignIn", dependencies: [ .product(name: "AppAuth", package: "AppAuth"), + .product(name: "FirebaseAppCheck", package: "Firebase"), .product(name: "GTMAppAuth", package: "GTMAppAuth"), .product(name: "GTMSessionFetcherCore", package: "GTMSessionFetcher"), ], @@ -106,6 +111,7 @@ let package = Package( "GoogleSignIn", "OCMock", .product(name: "AppAuth", package: "AppAuth"), + .product(name: "FirebaseAppCheck", package: "Firebase"), .product(name: "GTMAppAuth", package: "GTMAppAuth"), .product(name: "GTMSessionFetcherCore", package: "GTMSessionFetcher"), .product(name: "GULMethodSwizzler", package: "GoogleUtilities"), diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..344a6773 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj @@ -0,0 +1,434 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 4D8DB53AAE2F7D0055DCEA7F /* Pods_AppAttestExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */; }; + 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */; }; + 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */; }; + 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464052A1C3B3400BA8528 /* ContentView.swift */; }; + 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */; }; + 73BC0EB22A57609D00C3DDE5 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 73BC0EB12A57609D00C3DDE5 /* GoogleService-Info.plist */; }; + 73BD4BB52A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73BD4BB42A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1C96B5B2B34E31F1A1CEE95E /* Pods-AppAttestExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttestExample.release.xcconfig"; path = "Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample.release.xcconfig"; sourceTree = ""; }; + 73443A232A55F56900A4932E /* AppAttestExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppAttestExample.entitlements; sourceTree = ""; }; + 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = ""; }; + 73A464002A1C3B3400BA8528 /* AppAttestExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppAttestExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleApp.swift; sourceTree = ""; }; + 73A464052A1C3B3400BA8528 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 73BC0EB12A57609D00C3DDE5 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 73BD4BB42A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayAppCheckProviderFactory.swift; sourceTree = ""; }; + 7D9832F2FFAF408698660CA8 /* Pods-AppAttestExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttestExample.debug.xcconfig"; path = "Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample.debug.xcconfig"; sourceTree = ""; }; + 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppAttestExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 73A463FD2A1C3B3400BA8528 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D8DB53AAE2F7D0055DCEA7F /* Pods_AppAttestExample.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6B1005926777EEB3C903F93A /* Pods */ = { + isa = PBXGroup; + children = ( + 7D9832F2FFAF408698660CA8 /* Pods-AppAttestExample.debug.xcconfig */, + 1C96B5B2B34E31F1A1CEE95E /* Pods-AppAttestExample.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 73A463F72A1C3B3400BA8528 = { + isa = PBXGroup; + children = ( + 73A464022A1C3B3400BA8528 /* AppAttestExample */, + 73A464012A1C3B3400BA8528 /* Products */, + 6B1005926777EEB3C903F93A /* Pods */, + A73FBC2B93918F4B411815A1 /* Frameworks */, + ); + sourceTree = ""; + }; + 73A464012A1C3B3400BA8528 /* Products */ = { + isa = PBXGroup; + children = ( + 73A464002A1C3B3400BA8528 /* AppAttestExample.app */, + ); + name = Products; + sourceTree = ""; + }; + 73A464022A1C3B3400BA8528 /* AppAttestExample */ = { + isa = PBXGroup; + children = ( + 73443A232A55F56900A4932E /* AppAttestExample.entitlements */, + 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */, + 73BD4BB42A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift */, + 73A464052A1C3B3400BA8528 /* ContentView.swift */, + 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */, + 73BC0EB12A57609D00C3DDE5 /* GoogleService-Info.plist */, + 73A464092A1C3B3500BA8528 /* Preview Content */, + ); + path = AppAttestExample; + sourceTree = ""; + }; + 73A464092A1C3B3500BA8528 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + A73FBC2B93918F4B411815A1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 73A463FF2A1C3B3400BA8528 /* AppAttestExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 73A4640E2A1C3B3500BA8528 /* Build configuration list for PBXNativeTarget "AppAttestExample" */; + buildPhases = ( + D6AEC62E9810AEFD4C28F50F /* [CP] Check Pods Manifest.lock */, + 73A463FC2A1C3B3400BA8528 /* Sources */, + 73A463FD2A1C3B3400BA8528 /* Frameworks */, + 73A463FE2A1C3B3400BA8528 /* Resources */, + C031D9D83F25CB0CD2512F23 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AppAttestExample; + productName = AppAttestExample; + productReference = 73A464002A1C3B3400BA8528 /* AppAttestExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 73A463F82A1C3B3400BA8528 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + 73A463FF2A1C3B3400BA8528 = { + CreatedOnToolsVersion = 14.3; + }; + }; + }; + buildConfigurationList = 73A463FB2A1C3B3400BA8528 /* Build configuration list for PBXProject "AppAttestExample" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 73A463F72A1C3B3400BA8528; + productRefGroup = 73A464012A1C3B3400BA8528 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 73A463FF2A1C3B3400BA8528 /* AppAttestExample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 73A463FE2A1C3B3400BA8528 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */, + 73BC0EB22A57609D00C3DDE5 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + C031D9D83F25CB0CD2512F23 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + D6AEC62E9810AEFD4C28F50F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-AppAttestExample-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 73A463FC2A1C3B3400BA8528 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */, + 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */, + 73BD4BB52A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift in Sources */, + 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 73A4640C2A1C3B3500BA8528 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 73A4640D2A1C3B3500BA8528 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 73A4640F2A1C3B3500BA8528 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7D9832F2FFAF408698660CA8 /* Pods-AppAttestExample.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AppAttestExample/AppAttestExample.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AppAttestExample/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 73A464102A1C3B3500BA8528 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1C96B5B2B34E31F1A1CEE95E /* Pods-AppAttestExample.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AppAttestExample/AppAttestExample.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AppAttestExample/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 73A463FB2A1C3B3400BA8528 /* Build configuration list for PBXProject "AppAttestExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 73A4640C2A1C3B3500BA8528 /* Debug */, + 73A4640D2A1C3B3500BA8528 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 73A4640E2A1C3B3500BA8528 /* Build configuration list for PBXNativeTarget "AppAttestExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 73A4640F2A1C3B3500BA8528 /* Debug */, + 73A464102A1C3B3500BA8528 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 73A463F82A1C3B3400BA8528 /* Project object */; +} diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme new file mode 100644 index 00000000..43e79198 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExample.entitlements b/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExample.entitlements new file mode 100644 index 00000000..eccdd1d2 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExample.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.developer.devicecheck.appattest-environment + production + + diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift b/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift new file mode 100644 index 00000000..cdaa5db2 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import SwiftUI +import FirebaseCore +import FirebaseAppCheck +import GoogleSignIn + +class AppDelegate: NSObject, UIApplicationDelegate { + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil + ) -> Bool { + #if targetEnvironment(simulator) + let debugProvider = AppCheckDebugProviderFactory() + AppCheck.setAppCheckProviderFactory(debugProvider) + #else + AppCheck.setAppCheckProviderFactory(BirthdayAppCheckProviderFactory()) + #endif + FirebaseApp.configure() + + GIDSignIn.sharedInstance.configureWithCompletion { error in + if let error { + print("Error configuring `GIDSignIn` for Firebase App Check: \(error)") + } + } + + return true + } +} + +@main +struct AppAttestExampleApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/BirthdayAppCheckProviderFactory.swift b/Samples/Swift/AppAttestExample/AppAttestExample/BirthdayAppCheckProviderFactory.swift new file mode 100644 index 00000000..baf501b0 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/BirthdayAppCheckProviderFactory.swift @@ -0,0 +1,25 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FirebaseCore +import FirebaseAppCheck + +class BirthdayAppCheckProviderFactory: NSObject, AppCheckProviderFactory { + func createProvider(with app: FirebaseApp) -> AppCheckProvider? { + return AppAttestProvider(app: app) + } +} + diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/BirthdayLoader.swift b/Samples/Swift/AppAttestExample/AppAttestExample/BirthdayLoader.swift new file mode 100644 index 00000000..f3ac9811 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/BirthdayLoader.swift @@ -0,0 +1,111 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Foundation +import GoogleSignIn + +class BirthdayLoader { + /// The scope required to read a user's birthday. + static let birthdayReadScope = "https://www.googleapis.com/auth/user.birthday.read" + private let baseUrlString = "https://people.googleapis.com/v1/people/me" + private let personFieldsQuery = URLQueryItem(name: "personFields", value: "birthdays") + + private lazy var components: URLComponents? = { + var comps = URLComponents(string: baseUrlString) + comps?.queryItems = [personFieldsQuery] + return comps + }() + + private lazy var request: URLRequest? = { + guard let components = components, let url = components.url else { + return nil + } + return URLRequest(url: url) + }() + + private func sessionWithFreshToken(completion: @escaping (Result) -> Void) { + GIDSignIn.sharedInstance.currentUser?.refreshTokensIfNeeded { user, error in + guard let token = user?.accessToken.tokenString else { + completion(.failure(.couldNotRefreshToken)) + return + } + let config = URLSessionConfiguration.default + config.httpAdditionalHeaders = [ + "Authorization": "Bearer \(token)" + ] + let session = URLSession(configuration: config) + completion(.success(session)) + } + } + + func requestBirthday(completion: @escaping (Result) -> Void) { + guard let req = request else { + completion(.failure(Error.noRequest)) + return + } + sessionWithFreshToken { sessionResult in + switch sessionResult { + case .success(let session): + let task = session.dataTask(with: req) { data, response, error in + guard let data else { + completion(.failure(Error.noData)) + return + } + do { + let jsonData = try JSONSerialization.jsonObject(with: data) + guard let json = jsonData as? [String: Any] else { + completion(.failure(Error.jsonDataCannotCastToString)) + return + } + guard let birthdays = json["birthdays"] as? [[String: Any]], + let firstBday = birthdays.first?["date"] as? [String: Int], + let day = firstBday["day"], + let month = firstBday["month"] else { + completion(.failure(Error.noBirthday)) + return + } + completion(.success(Birthday(day: day, month: month))) + } catch { + completion(.failure(Error.noJSON)) + } + } + task.resume() + case .failure(let error): + completion(.failure(error)) + } + } + } +} + +extension BirthdayLoader { + enum Error: Swift.Error { + case noRequest + case noData + case noJSON + case jsonDataCannotCastToString + case noBirthday + case couldNotRefreshToken + } +} + +struct Birthday: CustomStringConvertible { + let day: Int + let month: Int + + var description: String { + return "Day: \(day); month: \(month)" + } +} diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/ContentView.swift b/Samples/Swift/AppAttestExample/AppAttestExample/ContentView.swift new file mode 100644 index 00000000..8db348c7 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/ContentView.swift @@ -0,0 +1,218 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import SwiftUI +import GoogleSignIn +import GoogleSignInSwift + +struct ContentView: View { + @State private var userInfo = "" + @State private var shouldPresentAlert = false + @State private var shouldPresentBirthdayAlert = false + @State private var errorInfo: ErrorInfo? + @State private var birthday: Birthday? + private let birthdayLoader = BirthdayLoader() + + var body: some View { + NavigationStack { + VStack { + HStack { + GoogleSignInButton(style: .wide) { + self.userInfo = "" + guard let rootViewController = self.rootViewController else { + print("No root view controller") + return + } + GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) { result, error in + guard let result else { + print("Error signing in: \(String(describing: error))") + return + } + print("Successfully signed in user") + self.userInfo = result.user.profile?.json ?? "" + } + } + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button(NSLocalizedString("Sign out", comment: "sign out")) { + GIDSignIn.sharedInstance.signOut() + clearState() + } + .disabled(userInfo.isEmpty) + } + ToolbarItem(placement: .navigationBarLeading) { + Button(NSLocalizedString("Disconnect", comment: "disconnect")) { + GIDSignIn.sharedInstance.disconnect { error in + if let error { + print("Disconnection error: \(error)") + return + } + print("Disconnected") + clearState() + } + } + .disabled(shouldDisableDisconnect) + } + ToolbarItem(placement: .bottomBar) { + Button(NSLocalizedString("Add Birthday Scope", comment: "Add Scope Button")) { + guard let rvc = rootViewController else { + print("No root view controller to use as presenter") + return + } + GIDSignIn.sharedInstance.currentUser?.addScopes( + [BirthdayLoader.birthdayReadScope], + presenting: rvc + ) { result, error in + shouldPresentAlert = true + if let result { + print("Successfully added scope: \(result)") + errorInfo = ErrorInfo.success + return + } + if let e = error { + print("Failed to add scope: \(e)") + if (e as NSError).code == GIDSignInError.scopesAlreadyGranted.rawValue { + errorInfo = ErrorInfo( + errorDescription: NSLocalizedString( + "Did not add scope", + comment: "no scope" + ), + failureReason: NSLocalizedString( + "User already added scope.", + comment: "already added scope" + ) + ) + } else { + errorInfo = ErrorInfo.unkownError + } + return + } + } + } + .disabled(userInfo.isEmpty) + } + ToolbarItem(placement: .bottomBar) { + Button(NSLocalizedString("Fetch Birthday", comment: "birthday")) { + birthdayLoader.requestBirthday { birthdayResult in + switch birthdayResult { + case .success(let birthday): + print(birthday) + self.birthday = birthday + errorInfo = ErrorInfo.birthday(with: birthday) + case .failure(let error): + print("Error fetching birthday: \(error)") + errorInfo = ErrorInfo( + errorDescription: (error as NSError).domain, + failureReason: String((error as NSError).code) + ) + } + shouldPresentBirthdayAlert = true + } + } + .disabled( + !(GIDSignIn + .sharedInstance + .currentUser? + .grantedScopes? + .contains(BirthdayLoader.birthdayReadScope) ?? false) + ) + .alert( + isPresented: $shouldPresentBirthdayAlert, + error: errorInfo) { info in + Text(info.errorDescription ?? "Unknown birthday fetch error") + } message: { info in + Text(info.birthday?.description ?? "No birthday") + } + } + } + .alert( + isPresented: $shouldPresentAlert, + error: errorInfo) { info in + Text(info.errorDescription ?? "Unknown Alert") + } message: { info in + Text(info.failureReason ?? "NA") + } + } + Text(userInfo) + } + .padding() + } + } +} + +private extension ContentView { + var rootViewController: UIViewController? { + return UIApplication.shared.connectedScenes + .filter({ $0.activationState == .foregroundActive }) + .compactMap { $0 as? UIWindowScene } + .compactMap { $0.keyWindow } + .first?.rootViewController + } + + var shouldDisableDisconnect: Bool { + guard !userInfo.isEmpty else { + return true + } + return GIDSignIn.sharedInstance.currentUser?.grantedScopes?.isEmpty ?? false + } + + func clearState() { + errorInfo = nil + shouldPresentAlert = false + userInfo = "" + } +} + +private extension GIDProfileData { + var json: String { + """ + success: { + Given Name: \(self.givenName ?? "None") + Family Name: \(self.familyName ?? "None") + Name: \(self.name) + Email: \(self.email) + Profile Photo: \(self.imageURL(withDimension: 1)?.absoluteString ?? "None"); + } + """ + } +} + +fileprivate struct ErrorInfo: LocalizedError { + static var unkownError: ErrorInfo { + return ErrorInfo( + errorDescription: NSLocalizedString("Unknown Error!", comment: "unknown error"), + failureReason: NSLocalizedString("NA", comment: "NA error string") + ) + } + static var success: ErrorInfo { + return ErrorInfo( + errorDescription: NSLocalizedString("Success!", comment: "no error"), + failureReason: NSLocalizedString("Added birthday read scope.", comment: "scope added") + ) + } + static func birthday(with bday: Birthday) -> ErrorInfo { + return ErrorInfo( + errorDescription: NSLocalizedString("Success!", comment: "no error"), + failureReason: NSLocalizedString("Added birthday read scope.", comment: "scope added"), + birthday: bday + ) + } + var errorDescription: String? + var failureReason: String? + var recoverySuggestion: String? + var helpAnchor: String? + var birthday: Birthday? +} diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/AccentColor.colorset/Contents.json b/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/AppIcon.appiconset/Contents.json b/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..13613e3e --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/Contents.json b/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Assets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Contents.json b/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Samples/Swift/AppAttestExample/Podfile b/Samples/Swift/AppAttestExample/Podfile new file mode 100644 index 00000000..a99c529f --- /dev/null +++ b/Samples/Swift/AppAttestExample/Podfile @@ -0,0 +1,12 @@ +pod 'GoogleSignIn', :path => '../../../', :testspecs => ['unit'] +pod 'GoogleSignInSwiftSupport', :path => '../../../', :testspecs => ['unit'] +project 'AppAttestExample.xcodeproj' + +use_frameworks! :linkage => :static + +target 'AppAttestExample' do + platform :ios, '14.0' + + pod 'FirebaseCore', '~> 10.0' + pod 'FirebaseAppCheck', '~> 10.0' +end diff --git a/Samples/Swift/AppAttestExample/README.md b/Samples/Swift/AppAttestExample/README.md new file mode 100644 index 00000000..e69de29b From 408616d2e1e9c28083045554210e9fced6f96ea7 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 4 Aug 2023 16:27:22 -0700 Subject: [PATCH 08/26] Depend on AppCheckCore instead of FirebaseAppCheck (#330) --- .github/workflows/unit_tests.yml | 1 + GoogleSignIn.podspec | 2 +- .../GIDAppCheck/API/GIDAppCheckProvider.h | 59 ----------------- .../Fake/GIDAppCheckProviderFake.h} | 21 +++--- .../Fake/GIDAppCheckProviderFake.m} | 21 +++--- .../GIDAppCheck/Implementations/GIDAppCheck.h | 41 ++++++++++-- .../GIDAppCheck/Implementations/GIDAppCheck.m | 66 ++++++++++++++----- .../API/GIDAppCheckTokenFetcher.h | 37 ----------- .../FIRAppCheck+GIDAppCheckTokenFetcher.h | 29 -------- GoogleSignIn/Sources/GIDSignIn.m | 24 ++++--- GoogleSignIn/Sources/GIDSignIn_Private.h | 4 +- .../Sources/Public/GoogleSignIn/GIDSignIn.h | 1 - GoogleSignIn/Tests/Unit/GIDAppCheckTest.m | 58 ++++++++-------- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 33 +++++----- Package.swift | 10 +-- .../project.pbxproj | 8 --- .../AppAttestExampleApp.swift | 10 --- .../BirthdayAppCheckProviderFactory.swift | 25 ------- .../AppAttestExample/Info.plist | 21 ++++++ Samples/Swift/AppAttestExample/Podfile | 4 +- 20 files changed, 197 insertions(+), 278 deletions(-) delete mode 100644 GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h rename GoogleSignIn/Sources/{GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h => GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h} (59%) rename GoogleSignIn/Sources/{GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.m => GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m} (60%) delete mode 100644 GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h delete mode 100644 GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h delete mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/BirthdayAppCheckProviderFactory.swift create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/Info.plist diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index d2ec3f52..f7cddc21 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -32,6 +32,7 @@ jobs: - name: Lint podspec using local source run: | pod lib lint ${{ matrix.podspec }} --verbose \ + --sources=https://github.com/firebase/SpecsDev.git,https://cdn.cocoapods.org/ \ ${{ matrix.includePodspecFlag }} ${{ matrix.flag }} spm-build-test: diff --git a/GoogleSignIn.podspec b/GoogleSignIn.podspec index 9b9a26bd..be36fce5 100644 --- a/GoogleSignIn.podspec +++ b/GoogleSignIn.podspec @@ -33,7 +33,7 @@ The Google Sign-In SDK allows users to sign in with their Google account from th ] s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' - s.dependency 'FirebaseAppCheck', '~> 10.0' + s.dependency 'AppCheckCore', '~> 0.1.0-alpha' s.dependency 'AppAuth', '>= 1.7.3', '< 2.0' s.dependency 'GTMAppAuth', '>= 4.1.1', '< 5.0' s.dependency 'GTMSessionFetcher/Core', '~> 3.3' diff --git a/GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h b/GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h deleted file mode 100644 index 7f934ec7..00000000 --- a/GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import - -NS_ASSUME_NONNULL_BEGIN - -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST -@protocol GIDAppCheckTokenFetcher; -@class FIRAppCheckToken; - -/// Interface providing the API for both pre-warming `GIDSignIn` to use Firebase App Check and -/// fetching the App Check token. -NS_AVAILABLE_IOS(14) -@protocol GIDAppCheckProvider - -/// Creates the instance of this App Check wrapper class. -/// -/// @param tokenFetcher The instance performing the Firebase App Check token requests. If `provider` -/// is nil, then we default to `FIRAppCheck`. -/// @param userDefaults The instance of `NSUserDefaults` that `GIDAppCheck` will use to store its -/// preparation status. If nil, `GIDAppCheck` will use `-[NSUserDefaults standardUserDefaults]`. -- (instancetype)initWithAppCheckTokenFetcher:(nullable id)tokenFetcher - userDefaults:(nullable NSUserDefaults *)userDefaults; - -/// Prewarms the library for App Check by asking Firebase App Check to generate the App Attest key -/// id and perform the initial attestation process (if needed). -/// -/// @param completion A `nullable` callback with a `nullable` `NSError` if preparation fails. -- (void)prepareForAppCheckWithCompletion:(nullable void (^)(NSError * _Nullable error))completion; - -/// Fetches the limited use Firebase token. -/// -/// @param completion A `nullable` callback with the `FIRAppCheckToken` if present, or an `NSError` -/// otherwise. -- (void)getLimitedUseTokenWithCompletion:(nullable void (^)(FIRAppCheckToken * _Nullable token, - NSError * _Nullable error))completion; - -/// Whether or not the App Attest key ID created and the attestation object has been fetched. -- (BOOL)isPrepared; - -@end - -#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST - -NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h similarity index 59% rename from GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h rename to GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h index ad994a92..2f0d64b8 100644 --- a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h @@ -16,26 +16,25 @@ #import #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST -#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h" +#import -@class FIRAppCheckToken; +@class GACAppCheckToken; NS_ASSUME_NONNULL_BEGIN -extern NSUInteger const kGIDAppCheckTokenFetcherTokenError; +extern NSUInteger const kGIDAppCheckProviderFakeError; NS_CLASS_AVAILABLE_IOS(14) -@interface GIDAppCheckTokenFetcherFake : NSObject +@interface GIDAppCheckProviderFake : NSObject -/// Creates an instance with the provided app check token and error. +/// Creates an instance conforming to `GACAppCheckProvider` with the provided app check token and +/// error. /// -/// This protocol is mainly used for testing purposes so that the token fetching from Firebase App -/// Check can be faked. -/// @param token The `FIRAppCheckToken` to pass into the completion called from -/// `limitedUseTokenWithCompletion:`. +/// @param token The `GACAppCheckToken` instance to pass into the completion called from +/// `getTokenWithCompletion:`. /// @param error The `NSError` to pass into the completion called from -/// `limitedUseTokenWithCompletion:`. -- (instancetype)initWithAppCheckToken:(nullable FIRAppCheckToken *)token +/// `getTokenWithCompletion:`. +- (instancetype)initWithAppCheckToken:(nullable GACAppCheckToken *)token error:(nullable NSError *)error; @end diff --git a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.m b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m similarity index 60% rename from GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.m rename to GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m index 20719a9e..8dc61095 100644 --- a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.m +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m @@ -12,25 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h" #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST -#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h" -@import FirebaseAppCheck; +#import -NSUInteger const kGIDAppCheckTokenFetcherTokenError = 1; +NSUInteger const kGIDAppCheckProviderFakeError = 1; -@interface GIDAppCheckTokenFetcherFake () +@interface GIDAppCheckProviderFake () -@property(nonatomic, strong, nullable) FIRAppCheckToken *token; +@property(nonatomic, strong, nullable) id token; @property(nonatomic, strong, nullable) NSError *error; @end -@implementation GIDAppCheckTokenFetcherFake +@implementation GIDAppCheckProviderFake -- (instancetype)initWithAppCheckToken:(nullable FIRAppCheckToken *)token +- (instancetype)initWithAppCheckToken:(nullable id)token error:(nullable NSError *)error { if (self = [super init]) { _token = token; @@ -39,10 +38,10 @@ - (instancetype)initWithAppCheckToken:(nullable FIRAppCheckToken *)token return self; } -- (void)limitedUseTokenWithCompletion:(void (^)(FIRAppCheckToken * _Nullable, - NSError * _Nullable))completion { +- (void)getTokenWithCompletion:(nonnull void (^)(GACAppCheckToken * _Nullable, + NSError * _Nullable))handler { dispatch_async(dispatch_get_main_queue(), ^{ - completion(self.token, self.error); + handler(self.token, self.error); }); } diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h index 96d63ea2..68d0a44e 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h @@ -19,17 +19,50 @@ #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST #import -#import "GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h" NS_ASSUME_NONNULL_BEGIN -@class FIRAppCheckToken; +@protocol GACAppCheckProvider; +@protocol GACAppCheckTokenProtocol; + extern NSString *const kGIDAppCheckPreparedKey; NS_CLASS_AVAILABLE_IOS(14) -@interface GIDAppCheck : NSObject +@interface GIDAppCheck : NSObject + +/// Creates the instance of this App Check wrapper class. +/// +/// The instance is created using `+[NSUserDefaults standardUserDefaults]` and the standard App +/// Check provider. +/// +/// @SeeAlso The App Check provider is constructed with `+[GIDAppCheck standardAppCheckProvider]`. +- (instancetype)init; + +/// Creates the instance of this App Check wrapper class. +/// +/// @param appCheckProvider The instance performing the Firebase App Check token requests. If `nil`, +/// then a default implementation will be used. +/// @param userDefaults The instance of `NSUserDefaults` that `GIDAppCheck` will use to store its +/// preparation status. If nil, `GIDAppCheck` will use `-[NSUserDefaults standardUserDefaults]`. +- (instancetype)initWithAppCheckProvider:(id)appCheckProvider + userDefaults:(NSUserDefaults *)userDefaults NS_DESIGNATED_INITIALIZER; + +/// Prewarms the library for App Check by asking Firebase App Check to generate the App Attest key +/// id and perform the initial attestation process (if needed). +/// +/// @param completion A `nullable` callback with a `nullable` `NSError` if preparation fails. +- (void)prepareForAppCheckWithCompletion:(nullable void (^)(NSError * _Nullable error))completion; + +/// Fetches the limited use Firebase token. +/// +/// @param completion A `nullable` callback with the `FIRAppCheckToken` if present, or an `NSError` +/// otherwise. +- (void)getLimitedUseTokenWithCompletion: + (nullable void (^)(id _Nullable token, + NSError * _Nullable error))completion; -- (instancetype)init NS_UNAVAILABLE; +/// Whether or not the App Attest key ID created and the attestation object has been fetched. +- (BOOL)isPrepared; @end diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m index 1b7a8095..3ef6e05b 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m @@ -15,23 +15,32 @@ */ #import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" -#import "GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h" -#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h" -#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h" - -@import FirebaseAppCheck; #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#import +#import +#import +#import + +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h" + NSErrorDomain const kGIDAppCheckErrorDomain = @"com.google.GIDAppCheck"; NSString *const kGIDAppCheckPreparedKey = @"com.google.GIDAppCheckPreparedKey"; +static NSString *const kGIDConfigClientIDKey = @"GIDClientID"; +static NSString *const kGIDAppAttestServiceName = @"GoogleSignIn-iOS"; +static NSString *const kGIDAppAttestResourceNameFormat = @"oauthClients/%@"; +static NSString *const kGIDAppAttestBaseURL = @"https://firebaseappcheck.googleapis.com/v1beta"; typedef void (^GIDAppCheckPrepareCompletion)(NSError * _Nullable); -typedef void (^GIDAppCheckTokenCompletion)(FIRAppCheckToken * _Nullable, NSError * _Nullable); +typedef void (^GIDAppCheckTokenCompletion)(id _Nullable, + NSError * _Nullable); @interface GIDAppCheck () -@property(nonatomic, strong) id tokenFetcher; +@property(nonatomic, strong) GACAppCheck *appCheck; @property(nonatomic, strong) dispatch_queue_t workerQueue; @property(nonatomic, strong) NSUserDefaults *userDefaults; @property(atomic, strong) NSMutableArray *prepareCompletions; @@ -41,11 +50,23 @@ @interface GIDAppCheck () @implementation GIDAppCheck -- (instancetype)initWithAppCheckTokenFetcher:(nullable id)tokenFetcher - userDefaults:(nullable NSUserDefaults *)userDefaults { +- (instancetype)init { + id provider = [GIDAppCheck standardAppCheckProvider]; + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + return [self initWithAppCheckProvider:provider userDefaults:userDefaults]; +} + +- (instancetype)initWithAppCheckProvider:(id)appCheckProvider + userDefaults:(NSUserDefaults *)userDefaults { if (self = [super init]) { - _tokenFetcher = tokenFetcher ?: [FIRAppCheck appCheck]; - _userDefaults = userDefaults ?: [NSUserDefaults standardUserDefaults]; + _appCheck = [[GACAppCheck alloc] initWithServiceName:kGIDConfigClientIDKey + resourceName:[GIDAppCheck appAttestResourceName] + appCheckProvider:appCheckProvider + settings:[[GACAppCheckSettings alloc] init] + tokenDelegate:nil + keychainAccessGroup:nil]; + + _userDefaults = userDefaults; _workerQueue = dispatch_queue_create("com.google.googlesignin.GIDAppCheckWorkerQueue", nil); _prepareCompletions = [NSMutableArray array]; _preparing = NO; @@ -89,8 +110,8 @@ - (void)prepareForAppCheckWithCompletion:(nullable GIDAppCheckPrepareCompletion) return; } - [self.tokenFetcher limitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, - NSError * _Nullable error) { + [self.appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, + NSError * _Nullable error) { NSError * __block maybeError = error; @synchronized (self) { if (!token && !error) { @@ -118,8 +139,8 @@ - (void)prepareForAppCheckWithCompletion:(nullable GIDAppCheckPrepareCompletion) - (void)getLimitedUseTokenWithCompletion:(nullable GIDAppCheckTokenCompletion)completion { dispatch_async(self.workerQueue, ^{ - [self.tokenFetcher limitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, - NSError * _Nullable error) { + [self.appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, + NSError * _Nullable error) { if (token) { [self.userDefaults setBool:YES forKey:kGIDAppCheckPreparedKey]; } @@ -130,6 +151,21 @@ - (void)getLimitedUseTokenWithCompletion:(nullable GIDAppCheckTokenCompletion)co }); } ++ (NSString *)appAttestResourceName { + NSString *clientID = [NSBundle.mainBundle objectForInfoDictionaryKey:kGIDConfigClientIDKey]; + return [NSString stringWithFormat:kGIDAppAttestResourceNameFormat, clientID]; +} + ++ (id)standardAppCheckProvider { + return [[GACAppAttestProvider alloc] initWithServiceName:kGIDAppAttestServiceName + resourceName:[GIDAppCheck appAttestResourceName] + baseURL:kGIDAppAttestBaseURL + APIKey:nil + keychainAccessGroup:nil + limitedUse:YES + requestHooks:nil]; +} + @end #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h deleted file mode 100644 index 57af940e..00000000 --- a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -NS_ASSUME_NONNULL_BEGIN - -@class FIRAppCheckToken; - -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - -NS_AVAILABLE_IOS(14) -@protocol GIDAppCheckTokenFetcher - -/// Get the limited use `FIRAppCheckToken`. -/// -/// @param completion A block that passes back the `FIRAppCheckToken` upon success or an error in -/// the case of any failure. -- (void)limitedUseTokenWithCompletion:(nullable void (^)(FIRAppCheckToken * _Nullable token, - NSError * _Nullable error))completion; - -@end - -#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST - -NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h b/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h deleted file mode 100644 index 0ec82aeb..00000000 --- a/GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/FIRAppCheck+GIDAppCheckTokenFetcher.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@import FirebaseAppCheck; -#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/API/GIDAppCheckTokenFetcher.h" - -NS_ASSUME_NONNULL_BEGIN - -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - -@interface FIRAppCheck (FIRAppCheck_GIDAppCheckTokenFetcher) -@end - -#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST - -NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index 25080d1c..d2ea2175 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -28,10 +28,9 @@ #import "GoogleSignIn/Sources/GIDScopes.h" #import "GoogleSignIn/Sources/GIDSignInCallbackSchemes.h" #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST -#import "FirebaseAppCheck/FIRAppCheckToken.h" -#import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" +#import #import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" -#import "GoogleSignIn/Sources/GIDAppCheck/API/GIDAppCheckProvider.h" +#import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" #import "GoogleSignIn/Sources/GIDAuthStateMigration.h" #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h" #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST @@ -170,7 +169,7 @@ @implementation GIDSignIn { // represent a sign in continuation. GIDSignInInternalOptions *_currentOptions; #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - id _appCheck API_AVAILABLE(ios(14)); + GIDAppCheck *_appCheck API_AVAILABLE(ios(14)); #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST // AppAuth configuration object. OIDServiceConfiguration *_appAuthConfiguration; @@ -460,10 +459,9 @@ + (GIDSignIn *)sharedInstance { [[GTMKeychainStore alloc] initWithItemName:kGTMAppAuthKeychainName]; #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST if (@available(iOS 14.0, *)) { - GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:nil - userDefaults:nil]; + GIDAppCheck *appCheck = [[GIDAppCheck alloc] init]; sharedInstance = [[self alloc] initWithKeychainStore:keychainStore - appCheckProvider:appCheck]; + appCheck:appCheck]; } #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST if (!sharedInstance) { @@ -479,9 +477,9 @@ + (GIDSignIn *)sharedInstance { - (void)configureWithCompletion:(nullable void (^)(NSError * _Nullable))completion { @synchronized(self) { [_appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { - if (completion) { - completion(error); - } + if (completion) { + completion(error); + } }]; } } @@ -531,10 +529,10 @@ - (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore { #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore - appCheckProvider:(id)appCheckProvider { + appCheck:(GIDAppCheck *)appCheck { self = [self initWithKeychainStore:keychainStore]; if (self) { - _appCheck = appCheckProvider; + _appCheck = appCheck; } return self; } @@ -646,7 +644,7 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp dispatch_time_t halfSecond = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC / 2); dispatch_after(halfSecond, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self->_appCheck getLimitedUseTokenWithCompletion: - ^(FIRAppCheckToken * _Nullable token, NSError * _Nullable error) { + ^(id _Nullable token, NSError * _Nullable error) { if (token) { additionalParameters[kClientAssertionTypeParameter] = kClientAssertionTypeParameterValue; diff --git a/GoogleSignIn/Sources/GIDSignIn_Private.h b/GoogleSignIn/Sources/GIDSignIn_Private.h index 7210ecd8..4072a4a0 100644 --- a/GoogleSignIn/Sources/GIDSignIn_Private.h +++ b/GoogleSignIn/Sources/GIDSignIn_Private.h @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN @class GIDGoogleUser; @class GIDSignInInternalOptions; @class GTMKeychainStore; -@protocol GIDAppCheckProvider; +@class GIDAppCheck; /// Represents a completion block that takes a `GIDSignInResult` on success or an error if the /// operation was unsuccessful. @@ -51,7 +51,7 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error); #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST /// Private initializer taking a `GTMKeychainStore` and `GIDAppCheckProvider`. - (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore - appCheckProvider:(id)appCheckProvider + appCheck:(GIDAppCheck *)appCheck API_AVAILABLE(ios(14)); #endif // TARGET_OS_IOS || !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h index c792c8ac..16ff5b55 100644 --- a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h +++ b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h @@ -26,7 +26,6 @@ @class GIDConfiguration; @class GIDGoogleUser; @class GIDSignInResult; -@protocol GIDAppCheckProvider; NS_ASSUME_NONNULL_BEGIN diff --git a/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m b/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m index f4e49d26..e84c9a84 100644 --- a/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m +++ b/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m @@ -17,13 +17,13 @@ #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST #import -#import "FirebaseAppCheck/FIRAppCheckToken.h" +#import #import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" -#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h" +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h" #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h" static NSUInteger const timeout = 1; -static NSString *const kUserDefaultsSuiteName = @"GIDAppCheckKeySuiteName"; +static NSString *const kUserDefaultsTestSuiteName = @"GIDAppCheckTestKeySuiteName"; NS_CLASS_AVAILABLE_IOS(14) @interface GIDAppCheckTest : XCTestCase @@ -36,27 +36,28 @@ @implementation GIDAppCheckTest - (void)setUp { [super setUp]; - _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:kUserDefaultsSuiteName]; + _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:kUserDefaultsTestSuiteName]; } - (void)tearDown { [super tearDown]; [self.userDefaults removeObjectForKey:kGIDAppCheckPreparedKey]; - [self.userDefaults removeSuiteNamed:kUserDefaultsSuiteName]; + [self.userDefaults removeSuiteNamed:kUserDefaultsTestSuiteName]; } - (void)testGetLimitedUseTokenFailure { XCTestExpectation *tokenFailExpectation = [self expectationWithDescription:@"App check token fail"]; NSError *expectedError = [NSError errorWithDomain:kGIDAppCheckErrorDomain - code:kGIDAppCheckTokenFetcherTokenError + code:kGIDAppCheckProviderFakeError userInfo:nil]; - GIDAppCheckTokenFetcherFake *tokenFetcher = - [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:nil error:expectedError]; - GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher - userDefaults:self.userDefaults]; - [appCheck getLimitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, + GIDAppCheckProviderFake *fakeProvider = + [[GIDAppCheckProviderFake alloc] initWithAppCheckToken:nil error:expectedError]; + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckProvider:fakeProvider + userDefaults:self.userDefaults]; + + [appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, NSError * _Nullable error) { XCTAssertNil(token); XCTAssertEqualObjects(expectedError, error); @@ -70,13 +71,14 @@ - (void)testIsPreparedError { XCTestExpectation *notAlreadyPreparedExpectation = [self expectationWithDescription:@"App check not already prepared error"]; - FIRAppCheckToken *expectedToken = [[FIRAppCheckToken alloc] initWithToken:@"foo" + GACAppCheckToken *expectedToken = [[GACAppCheckToken alloc] initWithToken:@"foo" expirationDate:[NSDate distantFuture]]; // It doesn't matter what we pass for the error since we will check `isPrepared` and make one - GIDAppCheckTokenFetcherFake *tokenFetcher = - [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:expectedToken error:nil]; - GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher - userDefaults:self.userDefaults]; + GIDAppCheckProviderFake *fakeProvider = + [[GIDAppCheckProviderFake alloc] initWithAppCheckToken:expectedToken error:nil]; + + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckProvider:fakeProvider + userDefaults:self.userDefaults]; [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { XCTAssertNil(error); @@ -88,6 +90,7 @@ - (void)testIsPreparedError { XCTestExpectation *alreadyPreparedExpectation = [self expectationWithDescription:@"App check already prepared error"]; + // Should be no error since multiple calls to prepare should be fine. [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { XCTAssertNil(error); [alreadyPreparedExpectation fulfill]; @@ -101,13 +104,14 @@ - (void)testIsPreparedError { - (void)testGetLimitedUseTokenSucceeds { XCTestExpectation *prepareExpectation = [self expectationWithDescription:@"Prepare for App Check expectation"]; - FIRAppCheckToken *expectedToken = [[FIRAppCheckToken alloc] initWithToken:@"foo" + + GACAppCheckToken *expectedToken = [[GACAppCheckToken alloc] initWithToken:@"foo" expirationDate:[NSDate distantFuture]]; - GIDAppCheckTokenFetcherFake *tokenFetcher = - [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:expectedToken error:nil]; - GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher - userDefaults:self.userDefaults]; + GIDAppCheckProviderFake *fakeProvider = + [[GIDAppCheckProviderFake alloc] initWithAppCheckToken:expectedToken error:nil]; + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckProvider:fakeProvider + userDefaults:self.userDefaults]; [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { XCTAssertNil(error); @@ -122,7 +126,7 @@ - (void)testGetLimitedUseTokenSucceeds { XCTestExpectation *getLimitedUseTokenSucceedsExpectation = [self expectationWithDescription:@"getLimitedUseToken should succeed"]; - [appCheck getLimitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, + [appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, NSError * _Nullable error) { XCTAssertNil(error); XCTAssertNotNil(token); @@ -142,13 +146,13 @@ - (void)testAsyncCompletions { XCTestExpectation *secondPrepareExpectation = [self expectationWithDescription:@"Second async prepare for App Check expectation"]; - FIRAppCheckToken *expectedToken = [[FIRAppCheckToken alloc] initWithToken:@"foo" + GACAppCheckToken *expectedToken = [[GACAppCheckToken alloc] initWithToken:@"foo" expirationDate:[NSDate distantFuture]]; - GIDAppCheckTokenFetcherFake *tokenFetcher = - [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:expectedToken error:nil]; - GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher - userDefaults:self.userDefaults]; + GIDAppCheckProviderFake *fakeProvider = + [[GIDAppCheckProviderFake alloc] initWithAppCheckToken:expectedToken error:nil]; + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckProvider:fakeProvider + userDefaults:self.userDefaults]; dispatch_async(dispatch_get_main_queue(), ^{ [appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index a0777ddb..400e09fb 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -34,9 +34,9 @@ #import "GoogleSignIn/Sources/GIDSignInPreferences.h" #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST -#import "FirebaseAppCheck/FIRAppCheckToken.h" +#import #import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" -#import "GoogleSignIn/Sources/GIDAppCheckTokenFetcher/Implementations/GIDAppCheckTokenFetcherFake.h" +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h" #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h" #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST @@ -389,23 +389,22 @@ - (void)testConfigureSucceeds { XCTestExpectation *configureSucceedsExpecation = [self expectationWithDescription:@"Configure succeeds expectation"]; - FIRAppCheckToken *token = [[FIRAppCheckToken alloc] initWithToken:@"foo" + GACAppCheckToken *token = [[GACAppCheckToken alloc] initWithToken:@"foo" expirationDate:[NSDate distantFuture]]; - GIDAppCheckTokenFetcherFake *tokenFetcher = - [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:token error:nil]; - GIDAppCheck *appCheckProvider = - [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher - userDefaults:_testUserDefaults]; + GIDAppCheckProviderFake *fakeProvider = + [[GIDAppCheckProviderFake alloc] initWithAppCheckToken:token error:nil]; + GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckProvider:fakeProvider + userDefaults:_testUserDefaults]; GIDSignIn *signIn = [[GIDSignIn alloc] initWithKeychainStore:_keychainStore - appCheckProvider:appCheckProvider]; + appCheck:appCheck]; [signIn configureWithCompletion:^(NSError * _Nullable error) { XCTAssertNil(error); [configureSucceedsExpecation fulfill]; }]; [self waitForExpectations:@[configureSucceedsExpecation] timeout:1]; - XCTAssertTrue(appCheckProvider.isPrepared); + XCTAssertTrue(appCheck.isPrepared); } } @@ -414,14 +413,14 @@ - (void)testConfigureFailsNoTokenOrError { XCTestExpectation *configureFailsExpecation = [self expectationWithDescription:@"Configure fails expectation"]; - GIDAppCheckTokenFetcherFake *tokenFetcher = - [[GIDAppCheckTokenFetcherFake alloc] initWithAppCheckToken:nil error:nil]; - GIDAppCheck *appCheckProvider = - [[GIDAppCheck alloc] initWithAppCheckTokenFetcher:tokenFetcher - userDefaults:_testUserDefaults]; + GIDAppCheckProviderFake *fakeProvider = + [[GIDAppCheckProviderFake alloc] initWithAppCheckToken:nil error:nil]; + GIDAppCheck *appCheck = + [[GIDAppCheck alloc] initWithAppCheckProvider:fakeProvider + userDefaults:_testUserDefaults]; GIDSignIn *signIn = [[GIDSignIn alloc] initWithKeychainStore:_keychainStore - appCheckProvider:appCheckProvider]; + appCheck:appCheck]; // `configureWithCompletion:` should fail if neither a token or error is present [signIn configureWithCompletion:^(NSError * _Nullable error) { @@ -431,7 +430,7 @@ - (void)testConfigureFailsNoTokenOrError { }]; [self waitForExpectations:@[configureFailsExpecation] timeout:1]; - XCTAssertFalse(appCheckProvider.isPrepared); + XCTAssertFalse(appCheck.isPrepared); } } #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/Package.swift b/Package.swift index 03a7d466..d9f48cef 100644 --- a/Package.swift +++ b/Package.swift @@ -46,9 +46,9 @@ let package = Package( url: "https://github.com/openid/AppAuth-iOS.git", from: "1.7.3"), .package( - name: "Firebase", - url: "https://github.com/firebase/firebase-ios-sdk.git", - "10.0.0" ..< "11.0.0"), + name: "AppCheck", + url: "https://github.com/google/app-check.git", + .branch("main")), .package( name: "GTMAppAuth", url: "https://github.com/google/GTMAppAuth.git", @@ -71,7 +71,7 @@ let package = Package( name: "GoogleSignIn", dependencies: [ .product(name: "AppAuth", package: "AppAuth"), - .product(name: "FirebaseAppCheck", package: "Firebase"), + .product(name: "AppCheckCore", package: "AppCheck"), .product(name: "GTMAppAuth", package: "GTMAppAuth"), .product(name: "GTMSessionFetcherCore", package: "GTMSessionFetcher"), ], @@ -111,7 +111,7 @@ let package = Package( "GoogleSignIn", "OCMock", .product(name: "AppAuth", package: "AppAuth"), - .product(name: "FirebaseAppCheck", package: "Firebase"), + .product(name: "AppCheckCore", package: "AppCheck"), .product(name: "GTMAppAuth", package: "GTMAppAuth"), .product(name: "GTMSessionFetcherCore", package: "GTMSessionFetcher"), .product(name: "GULMethodSwizzler", package: "GoogleUtilities"), diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj index 344a6773..342a46fe 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj @@ -12,8 +12,6 @@ 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */; }; 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464052A1C3B3400BA8528 /* ContentView.swift */; }; 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */; }; - 73BC0EB22A57609D00C3DDE5 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 73BC0EB12A57609D00C3DDE5 /* GoogleService-Info.plist */; }; - 73BD4BB52A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73BD4BB42A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -24,8 +22,6 @@ 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleApp.swift; sourceTree = ""; }; 73A464052A1C3B3400BA8528 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 73BC0EB12A57609D00C3DDE5 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - 73BD4BB42A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayAppCheckProviderFactory.swift; sourceTree = ""; }; 7D9832F2FFAF408698660CA8 /* Pods-AppAttestExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttestExample.debug.xcconfig"; path = "Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample.debug.xcconfig"; sourceTree = ""; }; 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppAttestExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -74,10 +70,8 @@ children = ( 73443A232A55F56900A4932E /* AppAttestExample.entitlements */, 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */, - 73BD4BB42A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift */, 73A464052A1C3B3400BA8528 /* ContentView.swift */, 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */, - 73BC0EB12A57609D00C3DDE5 /* GoogleService-Info.plist */, 73A464092A1C3B3500BA8528 /* Preview Content */, ); path = AppAttestExample; @@ -160,7 +154,6 @@ buildActionMask = 2147483647; files = ( 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */, - 73BC0EB22A57609D00C3DDE5 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -215,7 +208,6 @@ files = ( 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */, 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */, - 73BD4BB52A390CFE00A48E3C /* BirthdayAppCheckProviderFactory.swift in Sources */, 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift b/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift index cdaa5db2..a540b533 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift +++ b/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift @@ -15,8 +15,6 @@ */ import SwiftUI -import FirebaseCore -import FirebaseAppCheck import GoogleSignIn class AppDelegate: NSObject, UIApplicationDelegate { @@ -24,14 +22,6 @@ class AppDelegate: NSObject, UIApplicationDelegate { _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool { - #if targetEnvironment(simulator) - let debugProvider = AppCheckDebugProviderFactory() - AppCheck.setAppCheckProviderFactory(debugProvider) - #else - AppCheck.setAppCheckProviderFactory(BirthdayAppCheckProviderFactory()) - #endif - FirebaseApp.configure() - GIDSignIn.sharedInstance.configureWithCompletion { error in if let error { print("Error configuring `GIDSignIn` for Firebase App Check: \(error)") diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/BirthdayAppCheckProviderFactory.swift b/Samples/Swift/AppAttestExample/AppAttestExample/BirthdayAppCheckProviderFactory.swift deleted file mode 100644 index baf501b0..00000000 --- a/Samples/Swift/AppAttestExample/AppAttestExample/BirthdayAppCheckProviderFactory.swift +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import FirebaseCore -import FirebaseAppCheck - -class BirthdayAppCheckProviderFactory: NSObject, AppCheckProviderFactory { - func createProvider(with app: FirebaseApp) -> AppCheckProvider? { - return AppAttestProvider(app: app) - } -} - diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/Info.plist b/Samples/Swift/AppAttestExample/AppAttestExample/Info.plist new file mode 100644 index 00000000..b9179a51 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + com.googleusercontent.apps.665845761721-a9g0c1k6buv131av6nnmburou5scd63h + CFBundleURLSchemes + + com.googleusercontent.apps.665845761721-a9g0c1k6buv131av6nnmburou5scd63h + + + + GIDClientID + 665845761721-a9g0c1k6buv131av6nnmburou5scd63h.apps.googleusercontent.com + + diff --git a/Samples/Swift/AppAttestExample/Podfile b/Samples/Swift/AppAttestExample/Podfile index a99c529f..2a5c41a5 100644 --- a/Samples/Swift/AppAttestExample/Podfile +++ b/Samples/Swift/AppAttestExample/Podfile @@ -5,8 +5,6 @@ project 'AppAttestExample.xcodeproj' use_frameworks! :linkage => :static target 'AppAttestExample' do + pod 'AppCheckCore', :git => 'https://github.com/google/app-check.git', :tag => 'CocoaPods-0.1.0-alpha.1' platform :ios, '14.0' - - pod 'FirebaseCore', '~> 10.0' - pod 'FirebaseAppCheck', '~> 10.0' end From 150a195e46eee7ddd88b7b9ba52e189e9e349641 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 16 Aug 2023 17:14:15 -0700 Subject: [PATCH 09/26] Add a timed loader class (#331) --- .../UI/GIDActivityIndicatorViewController.h | 1 - .../UI/GIDActivityIndicatorViewController.m | 10 +- GoogleSignIn/Sources/GIDSignIn.m | 57 +++++---- .../Sources/GIDTimedLoader/GIDTimedLoader.h | 70 +++++++++++ .../Sources/GIDTimedLoader/GIDTimedLoader.m | 116 ++++++++++++++++++ .../project.pbxproj | 8 ++ 6 files changed, 230 insertions(+), 32 deletions(-) create mode 100644 GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h create mode 100644 GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m diff --git a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h index f0c77be9..2124d1cc 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h +++ b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h @@ -22,7 +22,6 @@ NS_ASSUME_NONNULL_BEGIN -NS_CLASS_AVAILABLE_IOS(14.0) /// A `UIViewController` presented onscreen to indicate to the user that GSI is performing blocking /// work. @interface GIDActivityIndicatorViewController : UIViewController diff --git a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m index c5615ac2..fa9888e6 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m +++ b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "GIDActivityIndicatorViewController.h" +#import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST @@ -25,7 +25,13 @@ @implementation GIDActivityIndicatorViewController - (void)viewDidLoad { [super viewDidLoad]; - _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge]; + UIActivityIndicatorViewStyle style; + if (@available(iOS 13.0, *)) { + style = UIActivityIndicatorViewStyleLarge; + } else { + style = UIActivityIndicatorViewStyleGray; + } + _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:style]; self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO; [self.activityIndicator startAnimating]; [self.view addSubview:self.activityIndicator]; diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index d2ea2175..bf832d3c 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -33,6 +33,7 @@ #import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" #import "GoogleSignIn/Sources/GIDAuthStateMigration.h" #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h" +#import "GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h" #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST #import "GoogleSignIn/Sources/GIDGoogleUser_Private.h" @@ -179,6 +180,10 @@ @implementation GIDSignIn { BOOL _restarting; // Keychain manager for GTMAppAuth GTMKeychainStore *_keychainStore; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + // The class used to manage presenting the loading screen for fetching app check tokens. + GIDTimedLoader *_timedLoader; +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST } #pragma mark - Public methods @@ -632,37 +637,31 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp [self additionalParametersFromOptions:options]; #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST if (@available(iOS 14.0, *)) { - if (_appCheck) { + // Only use `_appCheck` (created via singleton `+[GIDSignIn sharedInstance]` call) if + // `-[GIDAppCheck prepareForAppCheckWithCompletion:]` has been called + if ([_appCheck isPrepared]) { shouldCallCompletion = NO; - GIDActivityIndicatorViewController *activityVC = - [[GIDActivityIndicatorViewController alloc] init]; - [options.presentingViewController presentViewController:activityVC - animated:true - completion:^{ - // Ensure that the activity indicator shows for at least 1/2 second to prevent "flashing" - // TODO: Re-implement per: https://github.com/google/GoogleSignIn-iOS/issues/329 - dispatch_time_t halfSecond = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC / 2); - dispatch_after(halfSecond, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self->_appCheck getLimitedUseTokenWithCompletion: - ^(id _Nullable token, NSError * _Nullable error) { - if (token) { - additionalParameters[kClientAssertionTypeParameter] = - kClientAssertionTypeParameterValue; - additionalParameters[kClientAssertionParameter] = token.token; - OIDAuthorizationRequest *request = - [self authorizationRequestWithOptions:options - additionalParameters:additionalParameters]; - [activityVC.activityIndicator stopAnimating]; - [activityVC dismissViewControllerAnimated:YES completion:nil]; - completion(request, nil); - return; - } - [activityVC.activityIndicator stopAnimating]; - [activityVC dismissViewControllerAnimated:YES completion:nil]; - completion(nil, error); - return; + UIViewController *presentingVC = options.presentingViewController; + if (!_timedLoader) { + _timedLoader = [[GIDTimedLoader alloc] initWithPresentingViewController:presentingVC]; + } + [_timedLoader startTiming]; + [self->_appCheck getLimitedUseTokenWithCompletion: + ^(id _Nullable token, NSError * _Nullable error) { + OIDAuthorizationRequest *request = nil; + if (token) { + additionalParameters[kClientAssertionTypeParameter] = kClientAssertionTypeParameterValue; + additionalParameters[kClientAssertionParameter] = token.token; + request = [self authorizationRequestWithOptions:options + additionalParameters:additionalParameters]; + } + if (self->_timedLoader.animationStatus == GIDTimedLoaderAnimationStatusAnimating) { + [self->_timedLoader stopTimingWithCompletion:^{ + completion(request, error); }]; - }); + } else { + completion(request, error); + } }]; } } diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h new file mode 100644 index 00000000..bc108df0 --- /dev/null +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h @@ -0,0 +1,70 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +/// An enumeration detailing the states of the timed loader. +typedef NS_ENUM(NSUInteger, GIDTimedLoaderAnimationStatus) { + /// The timed loader has not started. + GIDTimedLoaderAnimationStatusNotStarted, + /// The timed loader's activity indicator is animating. + GIDTimedLoaderAnimationStatusAnimating, + /// The timed loader's activity indicator has stopped animating. + GIDTimedLoaderAnimationStatusStopped, +}; + +/// The minimum animation duration time for the timed loader's activity indicator. +extern CFTimeInterval const kGIDTimedLoaderMinAnimationDuration; +/// The maximum delay to wait before the time loader will display the loading activity indicator. +extern CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating; + +@class UIViewController; + +NS_ASSUME_NONNULL_BEGIN + +/// A type used to manage the presentation of a load screen for at least +/// `kGIDTimedLoaderMinAnimationDuration` to prevent flashing. +/// +/// `GIDTimedLoader` will also only show its loading screen until +/// `kGIDTimedLoaderMaxDelayBeforeAnimating` has expired. +@interface GIDTimedLoader : NSObject + +/// Created this timed loading controller with the provided presenting view controller, which will +/// be used for presenting hte loading view controller with the activity indicator. +- (instancetype)initWithPresentingViewController:(UIViewController *)presentingViewController; + +- (instancetype)init NS_UNAVAILABLE; + +/// Tells the controller to start keeping track of loading time. +- (void)startTiming; + +/// Tells the controller to stop keeping track of loading time. +/// +/// @param completion The block to invoke upon successfully stopping. +/// @note Use the completion parameter to, for example, present the UI that should be shown after +/// the work has completed. +- (void)stopTimingWithCompletion:(void (^)(void))completion; + +@property(nonatomic) GIDTimedLoaderAnimationStatus animationStatus; + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m new file mode 100644 index 00000000..c009dccf --- /dev/null +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m @@ -0,0 +1,116 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h" + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +@import UIKit; +@import CoreMedia; +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" +#import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" + +CFTimeInterval const kGIDTimedLoaderMinAnimationDuration = 1.0; +CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating = 0.5; + +@interface GIDTimedLoader () + +@property(nonatomic, strong) UIViewController *presentingViewController; +@property(nonatomic, strong) GIDActivityIndicatorViewController *loadingViewController; +@property(nonatomic, strong, nullable) NSTimer *loadingTimer; +/// Timestamp representing when the loading view controller was presented and started animating +@property(nonatomic) CFTimeInterval loadingTimeStamp; + +@end + +@implementation GIDTimedLoader + +- (instancetype)initWithPresentingViewController:(UIViewController *)presentingViewController { + if (self = [super init]) { + _presentingViewController = presentingViewController; + _loadingViewController = [[GIDActivityIndicatorViewController alloc] init]; + _animationStatus = GIDTimedLoaderAnimationStatusNotStarted; + } + return self; +} + +- (void)startTiming { + if (self.animationStatus == GIDTimedLoaderAnimationStatusAnimating) { + return; + } + + self.animationStatus = GIDTimedLoaderAnimationStatusAnimating; + self.loadingTimer = [NSTimer scheduledTimerWithTimeInterval:kGIDTimedLoaderMaxDelayBeforeAnimating + target:self + selector:@selector(presentLoadingViewController) + userInfo:nil + repeats:NO]; +} + +- (void)presentLoadingViewController { + if (self.animationStatus == GIDTimedLoaderAnimationStatusStopped) { + return; + } + self.animationStatus = GIDTimedLoaderAnimationStatusAnimating; + self.loadingTimeStamp = CACurrentMediaTime(); + dispatch_async(dispatch_get_main_queue(), ^{ + // Since this loading VC may be reused, the activity indicator may have been stopped; restart it + [self.loadingViewController.activityIndicator startAnimating]; + [self.presentingViewController presentViewController:self.loadingViewController + animated:YES + completion:nil]; + }); +} + +- (void)stopTimingWithCompletion:(void (^)(void))completion { + if (self.animationStatus != GIDTimedLoaderAnimationStatusAnimating) { + return; + } + + [self.loadingTimer invalidate]; + self.loadingTimer = nil; + + dispatch_time_t deadline = [self remainingDurationToAnimate]; + dispatch_after(deadline, dispatch_get_main_queue(), ^{ + self.animationStatus = GIDTimedLoaderAnimationStatusStopped; + [self.loadingViewController.activityIndicator stopAnimating]; + [self.loadingViewController dismissViewControllerAnimated:YES completion:nil]; + completion(); + }); +} + +- (dispatch_time_t)remainingDurationToAnimate { + // If we are not animating, then no need to wait + if (self.animationStatus != GIDTimedLoaderAnimationStatusAnimating) { + return 0; + } + + CFTimeInterval now = CACurrentMediaTime(); + CFTimeInterval durationWaited = now - self.loadingTimeStamp; + // If we have already waited for the minimum animation duration, then no need to wait + if (durationWaited >= kGIDTimedLoaderMinAnimationDuration) { + return 0; + } + + CFTimeInterval diff = kGIDTimedLoaderMinAnimationDuration - durationWaited; + int64_t diffNanos = diff * NSEC_PER_SEC; + dispatch_time_t timeToWait = dispatch_time(DISPATCH_TIME_NOW, diffNanos); + return timeToWait; +} + +@end + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj index 342a46fe..15473767 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 1C96B5B2B34E31F1A1CEE95E /* Pods-AppAttestExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttestExample.release.xcconfig"; path = "Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample.release.xcconfig"; sourceTree = ""; }; 73443A232A55F56900A4932E /* AppAttestExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppAttestExample.entitlements; sourceTree = ""; }; 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = ""; }; + 73A065612A786D10007BC7FC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 73A464002A1C3B3400BA8528 /* AppAttestExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppAttestExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleApp.swift; sourceTree = ""; }; 73A464052A1C3B3400BA8528 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -72,6 +73,7 @@ 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */, 73A464052A1C3B3400BA8528 /* ContentView.swift */, 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */, + 73A065612A786D10007BC7FC /* Info.plist */, 73A464092A1C3B3500BA8528 /* Preview Content */, ); path = AppAttestExample; @@ -339,10 +341,12 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = AppAttestExample/AppAttestExample.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AppAttestExample/Info.plist; @@ -359,6 +363,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Experimental App 0 Dev"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -373,10 +378,12 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = AppAttestExample/AppAttestExample.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AppAttestExample/Info.plist; @@ -393,6 +400,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Experimental App 0 Dev"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; From b400e6146a360e79d852e0c5353331cb176a3cb7 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 23 Aug 2023 17:19:36 -0700 Subject: [PATCH 10/26] Remove unnecessary parameter used in creating GACAppAttestProvider (#334) --- GoogleSignIn.podspec | 2 +- .../Implementations/Fake/GIDAppCheckProviderFake.m | 9 ++++++++- .../Sources/GIDAppCheck/Implementations/GIDAppCheck.m | 9 ++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/GoogleSignIn.podspec b/GoogleSignIn.podspec index be36fce5..8e148ea8 100644 --- a/GoogleSignIn.podspec +++ b/GoogleSignIn.podspec @@ -33,7 +33,7 @@ The Google Sign-In SDK allows users to sign in with their Google account from th ] s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' - s.dependency 'AppCheckCore', '~> 0.1.0-alpha' + s.dependency 'AppCheckCore', '~> 0.1.0-alpha.4' s.dependency 'AppAuth', '>= 1.7.3', '< 2.0' s.dependency 'GTMAppAuth', '>= 4.1.1', '< 5.0' s.dependency 'GTMSessionFetcher/Core', '~> 3.3' diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m index 8dc61095..0a3dfb1a 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m @@ -38,13 +38,20 @@ - (instancetype)initWithAppCheckToken:(nullable id)tok return self; } -- (void)getTokenWithCompletion:(nonnull void (^)(GACAppCheckToken * _Nullable, +- (void)getTokenWithCompletion:(nonnull void (^)(id _Nullable, NSError * _Nullable))handler { dispatch_async(dispatch_get_main_queue(), ^{ handler(self.token, self.error); }); } +- (void)getLimitedUseTokenWithCompletion:(void (^)(id _Nullable, + NSError * _Nullable))handler { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(self.token, self.error); + }); +} + @end #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m index 3ef6e05b..97a74241 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m @@ -110,8 +110,8 @@ - (void)prepareForAppCheckWithCompletion:(nullable GIDAppCheckPrepareCompletion) return; } - [self.appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, - NSError * _Nullable error) { + [self.appCheck limitedUseTokenWithCompletion:^(id _Nullable token, + NSError * _Nullable error) { NSError * __block maybeError = error; @synchronized (self) { if (!token && !error) { @@ -139,8 +139,8 @@ - (void)prepareForAppCheckWithCompletion:(nullable GIDAppCheckPrepareCompletion) - (void)getLimitedUseTokenWithCompletion:(nullable GIDAppCheckTokenCompletion)completion { dispatch_async(self.workerQueue, ^{ - [self.appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, - NSError * _Nullable error) { + [self.appCheck limitedUseTokenWithCompletion:^(id _Nullable token, + NSError * _Nullable error) { if (token) { [self.userDefaults setBool:YES forKey:kGIDAppCheckPreparedKey]; } @@ -162,7 +162,6 @@ + (NSString *)appAttestResourceName { baseURL:kGIDAppAttestBaseURL APIKey:nil keychainAccessGroup:nil - limitedUse:YES requestHooks:nil]; } From f6df40e3086d46793eed9e500802893fb858abf0 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Thu, 31 Aug 2023 14:53:21 -0700 Subject: [PATCH 11/26] Remove use of GACAppCheckTokenProtocol per AppCheckCore api changes (#337) --- GoogleSignIn.podspec | 2 +- .../Fake/GIDAppCheckProviderFake.m | 10 +++++----- .../GIDAppCheck/Implementations/GIDAppCheck.h | 4 ++-- .../GIDAppCheck/Implementations/GIDAppCheck.m | 20 +++++++++---------- GoogleSignIn/Sources/GIDSignIn.m | 4 ++-- GoogleSignIn/Tests/Unit/GIDAppCheckTest.m | 8 ++++---- Package.swift | 2 +- 7 files changed, 24 insertions(+), 26 deletions(-) diff --git a/GoogleSignIn.podspec b/GoogleSignIn.podspec index 8e148ea8..7c651903 100644 --- a/GoogleSignIn.podspec +++ b/GoogleSignIn.podspec @@ -33,7 +33,7 @@ The Google Sign-In SDK allows users to sign in with their Google account from th ] s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' - s.dependency 'AppCheckCore', '~> 0.1.0-alpha.4' + s.dependency 'AppCheckCore', '~> 0.1.0-alpha.6' s.dependency 'AppAuth', '>= 1.7.3', '< 2.0' s.dependency 'GTMAppAuth', '>= 4.1.1', '< 5.0' s.dependency 'GTMSessionFetcher/Core', '~> 3.3' diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m index 0a3dfb1a..6ca88bc0 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m @@ -22,14 +22,14 @@ @interface GIDAppCheckProviderFake () -@property(nonatomic, strong, nullable) id token; +@property(nonatomic, strong, nullable) GACAppCheckToken *token; @property(nonatomic, strong, nullable) NSError *error; @end @implementation GIDAppCheckProviderFake -- (instancetype)initWithAppCheckToken:(nullable id)token +- (instancetype)initWithAppCheckToken:(nullable GACAppCheckToken *)token error:(nullable NSError *)error { if (self = [super init]) { _token = token; @@ -38,14 +38,14 @@ - (instancetype)initWithAppCheckToken:(nullable id)tok return self; } -- (void)getTokenWithCompletion:(nonnull void (^)(id _Nullable, - NSError * _Nullable))handler { +- (void)getTokenWithCompletion:(void (^)(GACAppCheckToken * _Nullable, + NSError * _Nullable))handler { dispatch_async(dispatch_get_main_queue(), ^{ handler(self.token, self.error); }); } -- (void)getLimitedUseTokenWithCompletion:(void (^)(id _Nullable, +- (void)getLimitedUseTokenWithCompletion:(void (^)(GACAppCheckToken * _Nullable, NSError * _Nullable))handler { dispatch_async(dispatch_get_main_queue(), ^{ handler(self.token, self.error); diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h index 68d0a44e..dde7c9cd 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h @@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN @protocol GACAppCheckProvider; -@protocol GACAppCheckTokenProtocol; +@class GACAppCheckToken; extern NSString *const kGIDAppCheckPreparedKey; @@ -58,7 +58,7 @@ NS_CLASS_AVAILABLE_IOS(14) /// @param completion A `nullable` callback with the `FIRAppCheckToken` if present, or an `NSError` /// otherwise. - (void)getLimitedUseTokenWithCompletion: - (nullable void (^)(id _Nullable token, + (nullable void (^)(GACAppCheckToken * _Nullable token, NSError * _Nullable error))completion; /// Whether or not the App Attest key ID created and the attestation object has been fetched. diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m index 97a74241..2e4d2c60 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m @@ -20,7 +20,7 @@ #import #import -#import +#import #import #import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" @@ -35,7 +35,7 @@ static NSString *const kGIDAppAttestBaseURL = @"https://firebaseappcheck.googleapis.com/v1beta"; typedef void (^GIDAppCheckPrepareCompletion)(NSError * _Nullable); -typedef void (^GIDAppCheckTokenCompletion)(id _Nullable, +typedef void (^GIDAppCheckTokenCompletion)(GACAppCheckToken * _Nullable, NSError * _Nullable); @interface GIDAppCheck () @@ -110,17 +110,16 @@ - (void)prepareForAppCheckWithCompletion:(nullable GIDAppCheckPrepareCompletion) return; } - [self.appCheck limitedUseTokenWithCompletion:^(id _Nullable token, - NSError * _Nullable error) { - NSError * __block maybeError = error; + [self.appCheck limitedUseTokenWithCompletion:^(GACAppCheckTokenResult * _Nonnull result) { + NSError * __block maybeError = result.error; @synchronized (self) { - if (!token && !error) { + if (!result.token && !result.error) { maybeError = [NSError errorWithDomain:kGIDAppCheckErrorDomain code:kGIDAppCheckUnexpectedError userInfo:nil]; } - if (token) { + if (result.token) { [self.userDefaults setBool:YES forKey:kGIDAppCheckPreparedKey]; } @@ -139,13 +138,12 @@ - (void)prepareForAppCheckWithCompletion:(nullable GIDAppCheckPrepareCompletion) - (void)getLimitedUseTokenWithCompletion:(nullable GIDAppCheckTokenCompletion)completion { dispatch_async(self.workerQueue, ^{ - [self.appCheck limitedUseTokenWithCompletion:^(id _Nullable token, - NSError * _Nullable error) { - if (token) { + [self.appCheck limitedUseTokenWithCompletion:^(GACAppCheckTokenResult * _Nonnull result) { + if (result.token) { [self.userDefaults setBool:YES forKey:kGIDAppCheckPreparedKey]; } if (completion) { - completion(token, error); + completion(result.token, result.error); } }]; }); diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index bf832d3c..67e4cc9e 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -646,8 +646,8 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp _timedLoader = [[GIDTimedLoader alloc] initWithPresentingViewController:presentingVC]; } [_timedLoader startTiming]; - [self->_appCheck getLimitedUseTokenWithCompletion: - ^(id _Nullable token, NSError * _Nullable error) { + [self->_appCheck getLimitedUseTokenWithCompletion:^(GACAppCheckToken * _Nullable token, + NSError * _Nullable error) { OIDAuthorizationRequest *request = nil; if (token) { additionalParameters[kClientAssertionTypeParameter] = kClientAssertionTypeParameterValue; diff --git a/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m b/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m index e84c9a84..8ba90887 100644 --- a/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m +++ b/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m @@ -45,7 +45,7 @@ - (void)tearDown { [self.userDefaults removeSuiteNamed:kUserDefaultsTestSuiteName]; } -- (void)testGetLimitedUseTokenFailure { +- (void)testGetLimitedUseTokenFailureReturnsPlaceholder { XCTestExpectation *tokenFailExpectation = [self expectationWithDescription:@"App check token fail"]; NSError *expectedError = [NSError errorWithDomain:kGIDAppCheckErrorDomain @@ -57,9 +57,9 @@ - (void)testGetLimitedUseTokenFailure { GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckProvider:fakeProvider userDefaults:self.userDefaults]; - [appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, + [appCheck getLimitedUseTokenWithCompletion:^(GACAppCheckToken * _Nullable token, NSError * _Nullable error) { - XCTAssertNil(token); + XCTAssertNotNil(token); // If there is an error, we expect a placeholder token XCTAssertEqualObjects(expectedError, error); [tokenFailExpectation fulfill]; }]; @@ -126,7 +126,7 @@ - (void)testGetLimitedUseTokenSucceeds { XCTestExpectation *getLimitedUseTokenSucceedsExpectation = [self expectationWithDescription:@"getLimitedUseToken should succeed"]; - [appCheck getLimitedUseTokenWithCompletion:^(id _Nullable token, + [appCheck getLimitedUseTokenWithCompletion:^(GACAppCheckToken * _Nullable token, NSError * _Nullable error) { XCTAssertNil(error); XCTAssertNotNil(token); diff --git a/Package.swift b/Package.swift index d9f48cef..61590a4e 100644 --- a/Package.swift +++ b/Package.swift @@ -48,7 +48,7 @@ let package = Package( .package( name: "AppCheck", url: "https://github.com/google/app-check.git", - .branch("main")), + .branch("CocoaPods-0.1.0-alpha.6")), .package( name: "GTMAppAuth", url: "https://github.com/google/GTMAppAuth.git", From 788411fbdde0338c3dda8645cf2ee5f86c6145fb Mon Sep 17 00:00:00 2001 From: mdmathias Date: Tue, 5 Sep 2023 14:41:38 -0700 Subject: [PATCH 12/26] Update GIDSignIn to handle placeholder app check tokens (#335) --- GoogleSignIn/Sources/GIDSignIn.m | 26 ++++++++++++++----- .../xcschemes/AppAttestExample.xcscheme | 7 ----- Samples/Swift/AppAttestExample/Podfile | 5 +++- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index 67e4cc9e..c544e588 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -183,6 +183,8 @@ @implementation GIDSignIn { #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST // The class used to manage presenting the loading screen for fetching app check tokens. GIDTimedLoader *_timedLoader; + // Flag indicating developer's intent to use App Check. + BOOL _configureAppCheckCalled; #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST } @@ -481,6 +483,7 @@ + (GIDSignIn *)sharedInstance { #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - (void)configureWithCompletion:(nullable void (^)(NSError * _Nullable))completion { @synchronized(self) { + _configureAppCheckCalled = YES; [_appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { if (completion) { completion(error); @@ -538,6 +541,7 @@ - (instancetype)initWithKeychainStore:(GTMKeychainStore *)keychainStore self = [self initWithKeychainStore:keychainStore]; if (self) { _appCheck = appCheck; + _configureAppCheckCalled = NO; } return self; } @@ -632,15 +636,18 @@ - (void)authenticateInteractivelyWithOptions:(GIDSignInInternalOptions *)options - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options completion: (void (^)(OIDAuthorizationRequest *_Nullable request, NSError *_Nullable error))completion { - BOOL shouldCallCompletion = YES; + BOOL shouldCreateAuthRequest = YES; NSMutableDictionary *additionalParameters = [self additionalParametersFromOptions:options]; #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST if (@available(iOS 14.0, *)) { // Only use `_appCheck` (created via singleton `+[GIDSignIn sharedInstance]` call) if - // `-[GIDAppCheck prepareForAppCheckWithCompletion:]` has been called - if ([_appCheck isPrepared]) { - shouldCallCompletion = NO; + // `GIDAppCheck` has been successfully prepared OR if the developer has attempted to configure. + // If former is false and the latter true, then preparation step failed for some reason; we + // still want to try to pass along the app check token (it just may take longer since the + // pre-warm step failed). + if ([_appCheck isPrepared] || _configureAppCheckCalled) { + shouldCreateAuthRequest = NO; UIViewController *presentingVC = options.presentingViewController; if (!_timedLoader) { _timedLoader = [[GIDTimedLoader alloc] initWithPresentingViewController:presentingVC]; @@ -652,9 +659,14 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp if (token) { additionalParameters[kClientAssertionTypeParameter] = kClientAssertionTypeParameterValue; additionalParameters[kClientAssertionParameter] = token.token; - request = [self authorizationRequestWithOptions:options - additionalParameters:additionalParameters]; } + #if DEBUG + if (error) { + NSLog(@"[Google Sign-In iOS]: Error retrieving App Check limited use token: %@", error); + } + #endif + request = [self authorizationRequestWithOptions:options + additionalParameters:additionalParameters]; if (self->_timedLoader.animationStatus == GIDTimedLoaderAnimationStatusAnimating) { [self->_timedLoader stopTimingWithCompletion:^{ completion(request, error); @@ -666,7 +678,7 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp } } #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST - if (shouldCallCompletion) { + if (shouldCreateAuthRequest) { OIDAuthorizationRequest *request = [self authorizationRequestWithOptions:options additionalParameters:additionalParameters]; completion(request, nil); diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme index 43e79198..f2c125f8 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme @@ -49,13 +49,6 @@ ReferencedContainer = "container:AppAttestExample.xcodeproj"> - - - - '../../../', :testspecs => ['unit'] pod 'GoogleSignInSwiftSupport', :path => '../../../', :testspecs => ['unit'] project 'AppAttestExample.xcodeproj' @@ -5,6 +8,6 @@ project 'AppAttestExample.xcodeproj' use_frameworks! :linkage => :static target 'AppAttestExample' do - pod 'AppCheckCore', :git => 'https://github.com/google/app-check.git', :tag => 'CocoaPods-0.1.0-alpha.1' + pod 'AppCheckCore' platform :ios, '14.0' end From d966409644f22926a64aca9134b74dc31e04545f Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 8 Sep 2023 16:28:11 -0700 Subject: [PATCH 13/26] Support using a debug app check provider during configuration (#336) --- .gitignore | 1 + GoogleSignIn.podspec | 2 +- .../Fake/GIDAppCheckProviderFake.h | 3 +- .../Fake/GIDAppCheckProviderFake.m | 5 +-- .../GIDAppCheck/Implementations/GIDAppCheck.h | 22 ++++++---- .../GIDAppCheck/Implementations/GIDAppCheck.m | 26 ++++++++--- GoogleSignIn/Sources/GIDSignIn.m | 14 +++++- .../Sources/Public/GoogleSignIn/GIDSignIn.h | 17 ++++++- GoogleSignIn/Tests/Unit/GIDAppCheckTest.m | 6 +-- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 2 +- .../project.pbxproj | 7 +++ .../AppAttestExampleApp.swift | 15 ++++++- .../AppCheckSecretReader.swift | 44 +++++++++++++++++++ .../AppAttestExample/Info.plist | 2 + 14 files changed, 137 insertions(+), 29 deletions(-) create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/AppCheckSecretReader.swift diff --git a/.gitignore b/.gitignore index 538419c3..103cb405 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ Podfile.lock # Firebase App Check Example **/GoogleService-Info.plist +**/AppCheckSecrets.xcconfig diff --git a/GoogleSignIn.podspec b/GoogleSignIn.podspec index 7c651903..ffdce777 100644 --- a/GoogleSignIn.podspec +++ b/GoogleSignIn.podspec @@ -33,7 +33,7 @@ The Google Sign-In SDK allows users to sign in with their Google account from th ] s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' - s.dependency 'AppCheckCore', '~> 0.1.0-alpha.6' + s.dependency 'AppCheckCore', '~> 0.1.0-alpha.9' s.dependency 'AppAuth', '>= 1.7.3', '< 2.0' s.dependency 'GTMAppAuth', '>= 4.1.1', '< 5.0' s.dependency 'GTMSessionFetcher/Core', '~> 3.3' diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h index 2f0d64b8..e294273f 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.h @@ -31,7 +31,8 @@ NS_CLASS_AVAILABLE_IOS(14) /// error. /// /// @param token The `GACAppCheckToken` instance to pass into the completion called from -/// `getTokenWithCompletion:`. +/// `getTokenWithCompletion:`. Use `nil` if you would like a placeholder token from +/// AppCheckCore. /// @param error The `NSError` to pass into the completion called from /// `getTokenWithCompletion:`. - (instancetype)initWithAppCheckToken:(nullable GACAppCheckToken *)token diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m index 6ca88bc0..903fc3ff 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/Fake/GIDAppCheckProviderFake.m @@ -38,14 +38,13 @@ - (instancetype)initWithAppCheckToken:(nullable GACAppCheckToken *)token return self; } -- (void)getTokenWithCompletion:(void (^)(GACAppCheckToken * _Nullable, - NSError * _Nullable))handler { +- (void)getTokenWithCompletion:(void (^)(GACAppCheckToken *, NSError * _Nullable))handler { dispatch_async(dispatch_get_main_queue(), ^{ handler(self.token, self.error); }); } -- (void)getLimitedUseTokenWithCompletion:(void (^)(GACAppCheckToken * _Nullable, +- (void)getLimitedUseTokenWithCompletion:(void (^)(GACAppCheckToken *, NSError * _Nullable))handler { dispatch_async(dispatch_get_main_queue(), ^{ handler(self.token, self.error); diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h index dde7c9cd..6b7d1d92 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h @@ -30,13 +30,19 @@ extern NSString *const kGIDAppCheckPreparedKey; NS_CLASS_AVAILABLE_IOS(14) @interface GIDAppCheck : NSObject -/// Creates the instance of this App Check wrapper class. +- (instancetype)init NS_UNAVAILABLE; + +/// Creates the instance of this App Check wrapper class using `GACAppCheckDebugProvider`. /// -/// The instance is created using `+[NSUserDefaults standardUserDefaults]` and the standard App -/// Check provider. +/// @param APIKey The API Key to use when creating the debug App Check provider. +/// +/// The instance is created using `+[NSUserDefaults standardUserDefaults]`. ++ (instancetype)appCheckUsingDebugProviderWithAPIKey:(NSString *)APIKey; + +/// Creates the instance of this App Check wrapper class using `GACAppAttestProvider`. /// -/// @SeeAlso The App Check provider is constructed with `+[GIDAppCheck standardAppCheckProvider]`. -- (instancetype)init; +/// The instance is created using `+[NSUserDefaults standardUserDefaults]`. ++ (instancetype)appCheckUsingAppAttestProvider; /// Creates the instance of this App Check wrapper class. /// @@ -55,11 +61,9 @@ NS_CLASS_AVAILABLE_IOS(14) /// Fetches the limited use Firebase token. /// -/// @param completion A `nullable` callback with the `FIRAppCheckToken` if present, or an `NSError` -/// otherwise. +/// @param completion A `nullable` callback with the `FIRAppCheckToken`, or an `NSError` otherwise. - (void)getLimitedUseTokenWithCompletion: - (nullable void (^)(GACAppCheckToken * _Nullable token, - NSError * _Nullable error))completion; + (nullable void (^)(GACAppCheckToken *token, NSError * _Nullable error))completion; /// Whether or not the App Attest key ID created and the attestation object has been fetched. - (BOOL)isPrepared; diff --git a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m index 2e4d2c60..7ac20947 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m +++ b/GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.m @@ -22,6 +22,7 @@ #import #import #import +#import #import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDAppCheckError.h" @@ -35,8 +36,7 @@ static NSString *const kGIDAppAttestBaseURL = @"https://firebaseappcheck.googleapis.com/v1beta"; typedef void (^GIDAppCheckPrepareCompletion)(NSError * _Nullable); -typedef void (^GIDAppCheckTokenCompletion)(GACAppCheckToken * _Nullable, - NSError * _Nullable); +typedef void (^GIDAppCheckTokenCompletion)(GACAppCheckToken *,NSError * _Nullable); @interface GIDAppCheck () @@ -50,10 +50,14 @@ @interface GIDAppCheck () @implementation GIDAppCheck -- (instancetype)init { - id provider = [GIDAppCheck standardAppCheckProvider]; - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - return [self initWithAppCheckProvider:provider userDefaults:userDefaults]; ++ (instancetype)appCheckUsingDebugProviderWithAPIKey:(NSString *)APIKey { + return [[self alloc] initWithAppCheckProvider:[GIDAppCheck debugAppCheckProviderWithAPIKey:APIKey] + userDefaults:[NSUserDefaults standardUserDefaults]]; +} + ++ (instancetype)appCheckUsingAppAttestProvider { + return [[self alloc] initWithAppCheckProvider:[GIDAppCheck appAttestProvider] + userDefaults:[NSUserDefaults standardUserDefaults]]; } - (instancetype)initWithAppCheckProvider:(id)appCheckProvider @@ -154,7 +158,7 @@ + (NSString *)appAttestResourceName { return [NSString stringWithFormat:kGIDAppAttestResourceNameFormat, clientID]; } -+ (id)standardAppCheckProvider { ++ (id)appAttestProvider { return [[GACAppAttestProvider alloc] initWithServiceName:kGIDAppAttestServiceName resourceName:[GIDAppCheck appAttestResourceName] baseURL:kGIDAppAttestBaseURL @@ -163,6 +167,14 @@ + (NSString *)appAttestResourceName { requestHooks:nil]; } ++ (id)debugAppCheckProviderWithAPIKey:(NSString *)APIKey { + return [[GACAppCheckDebugProvider alloc] initWithServiceName:kGIDAppAttestServiceName + resourceName:[GIDAppCheck appAttestResourceName] + baseURL:kGIDAppAttestBaseURL + APIKey:APIKey + requestHooks:nil]; +} + @end #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index c544e588..0f16caa2 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -466,7 +466,7 @@ + (GIDSignIn *)sharedInstance { [[GTMKeychainStore alloc] initWithItemName:kGTMAppAuthKeychainName]; #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST if (@available(iOS 14.0, *)) { - GIDAppCheck *appCheck = [[GIDAppCheck alloc] init]; + GIDAppCheck *appCheck = [GIDAppCheck appCheckUsingAppAttestProvider]; sharedInstance = [[self alloc] initWithKeychainStore:keychainStore appCheck:appCheck]; } @@ -491,6 +491,18 @@ - (void)configureWithCompletion:(nullable void (^)(NSError * _Nullable))completi }]; } } + +- (void)configureDebugProviderWithAPIKey:(NSString *)APIKey + completion:(nullable void (^)(NSError * _Nullable))completion { + @synchronized(self) { + _appCheck = [GIDAppCheck appCheckUsingDebugProviderWithAPIKey:APIKey]; + [_appCheck prepareForAppCheckWithCompletion:^(NSError * _Nullable error) { + if (completion) { + completion(error); + } + }]; + } +} #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST #pragma mark - Private methods diff --git a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h index 16ff5b55..2cdcbe24 100644 --- a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h +++ b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h @@ -71,14 +71,27 @@ typedef NS_ERROR_ENUM(kGIDSignInErrorDomain, GIDSignInErrorCode) { /// Configures `GIDSignIn` for use. /// /// @param completion A nullable callback block passing back any error arising from the -/// configuration process if any exists. +/// configuration process if any exists. /// /// Call this method on `GIDSignIn` prior to use and as early as possible. This method generates App /// Attest key IDs and the attestation object eagerly to minimize latency later on during the sign /// in or add scopes flows. - (void)configureWithCompletion:(nullable void (^)(NSError * _Nullable error))completion +NS_SWIFT_NAME(configure(completion:)); + +/// Configures `GIDSignIn` for use in debug or test environments. +/// +/// @param APIKey The API Key to use during configuration of the App Check debug provider. +/// @param completion A nullable callback block passing back any error arising from the +/// configuration process if any exists. +/// +/// Call this method on `GIDSignIn` prior to use and as early as possible. This method generates App +/// Attest key IDs and the attestation object eagerly to minimize latency later on during the sign +/// in or add scopes flows. +- (void)configureDebugProviderWithAPIKey:(NSString *)APIKey + completion:(nullable void (^)(NSError * _Nullable error))completion API_AVAILABLE(ios(14)) -NS_SWIFT_NAME(configureWithCompletion(completion:)); +NS_SWIFT_NAME(configureDebugProvider(withAPIKey:completion:)); #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m b/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m index 8ba90887..7c0610ae 100644 --- a/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m +++ b/GoogleSignIn/Tests/Unit/GIDAppCheckTest.m @@ -57,10 +57,10 @@ - (void)testGetLimitedUseTokenFailureReturnsPlaceholder { GIDAppCheck *appCheck = [[GIDAppCheck alloc] initWithAppCheckProvider:fakeProvider userDefaults:self.userDefaults]; - [appCheck getLimitedUseTokenWithCompletion:^(GACAppCheckToken * _Nullable token, + [appCheck getLimitedUseTokenWithCompletion:^(GACAppCheckToken *token, NSError * _Nullable error) { - XCTAssertNotNil(token); // If there is an error, we expect a placeholder token XCTAssertEqualObjects(expectedError, error); + XCTAssertNotNil(token); // If there is an error, we expect a placeholder token [tokenFailExpectation fulfill]; }]; @@ -126,7 +126,7 @@ - (void)testGetLimitedUseTokenSucceeds { XCTestExpectation *getLimitedUseTokenSucceedsExpectation = [self expectationWithDescription:@"getLimitedUseToken should succeed"]; - [appCheck getLimitedUseTokenWithCompletion:^(GACAppCheckToken * _Nullable token, + [appCheck getLimitedUseTokenWithCompletion:^(GACAppCheckToken *token, NSError * _Nullable error) { XCTAssertNil(error); XCTAssertNotNil(token); diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index 400e09fb..0f35dc6b 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -422,7 +422,7 @@ - (void)testConfigureFailsNoTokenOrError { GIDSignIn *signIn = [[GIDSignIn alloc] initWithKeychainStore:_keychainStore appCheck:appCheck]; - // `configureWithCompletion:` should fail if neither a token or error is present + // Should fail if missing both token and error [signIn configureWithCompletion:^(NSError * _Nullable error) { XCTAssertNotNil(error); XCTAssertEqual(error.code, kGIDAppCheckUnexpectedError); diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj index 15473767..e976444d 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 4D8DB53AAE2F7D0055DCEA7F /* Pods_AppAttestExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */; }; + 738B4A322AA8FE800056885D /* AppCheckSecretReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */; }; 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */; }; 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */; }; 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464052A1C3B3400BA8528 /* ContentView.swift */; }; @@ -17,6 +18,8 @@ /* Begin PBXFileReference section */ 1C96B5B2B34E31F1A1CEE95E /* Pods-AppAttestExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttestExample.release.xcconfig"; path = "Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample.release.xcconfig"; sourceTree = ""; }; 73443A232A55F56900A4932E /* AppAttestExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppAttestExample.entitlements; sourceTree = ""; }; + 738B4A302AA7EB840056885D /* AppCheckSecrets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppCheckSecrets.xcconfig; sourceTree = ""; }; + 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCheckSecretReader.swift; sourceTree = ""; }; 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = ""; }; 73A065612A786D10007BC7FC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 73A464002A1C3B3400BA8528 /* AppAttestExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppAttestExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -73,6 +76,8 @@ 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */, 73A464052A1C3B3400BA8528 /* ContentView.swift */, 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */, + 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */, + 738B4A302AA7EB840056885D /* AppCheckSecrets.xcconfig */, 73A065612A786D10007BC7FC /* Info.plist */, 73A464092A1C3B3500BA8528 /* Preview Content */, ); @@ -209,6 +214,7 @@ buildActionMask = 2147483647; files = ( 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */, + 738B4A322AA8FE800056885D /* AppCheckSecretReader.swift in Sources */, 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */, 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */, ); @@ -219,6 +225,7 @@ /* Begin XCBuildConfiguration section */ 73A4640C2A1C3B3500BA8528 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 738B4A302AA7EB840056885D /* AppCheckSecrets.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift b/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift index a540b533..46157e46 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift +++ b/Samples/Swift/AppAttestExample/AppAttestExample/AppAttestExampleApp.swift @@ -22,11 +22,24 @@ class AppDelegate: NSObject, UIApplicationDelegate { _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool { - GIDSignIn.sharedInstance.configureWithCompletion { error in + #if targetEnvironment(simulator) + let secretReader = AppCheckSecretReader() + guard let APIKey = secretReader.APIKey else { + print("Unable to read API key from bundle or environment") + return true + } + GIDSignIn.sharedInstance.configureDebugProvider(withAPIKey: APIKey) { error in + if let error { + print("Error configuring `GIDSignIn` for Firebase App Check: \(error)") + } + } + #else + GIDSignIn.sharedInstance.configure { error in if let error { print("Error configuring `GIDSignIn` for Firebase App Check: \(error)") } } + #endif return true } diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/AppCheckSecretReader.swift b/Samples/Swift/AppAttestExample/AppAttestExample/AppCheckSecretReader.swift new file mode 100644 index 00000000..bdcab6f1 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExample/AppCheckSecretReader.swift @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Foundation + +struct AppCheckSecretReader { + private let APIKeyName = "APP_CHECK_WEB_API_KEY" + + var APIKey: String? { + return APIKeyFromBundle ?? APIKeyFromEnvironment + } + + /// Method for retrieving API key from environment variable used during CI tests + private var APIKeyFromEnvironment: String? { + guard let APIKey = ProcessInfo.processInfo.environment[APIKeyName], !APIKey.isEmpty else { + print("Failed to get \(APIKeyName) from environment.") + return nil + } + return APIKey + } + + /// Method for retrieving API key from the bundle during simulator or debug builds + private var APIKeyFromBundle: String? { + guard let APIKey = Bundle.main.infoDictionary?[APIKeyName] as? String, + !APIKey.isEmpty else { + print("Failed to get \(APIKeyName) from Bundle.") + return nil + } + return APIKey + } +} diff --git a/Samples/Swift/AppAttestExample/AppAttestExample/Info.plist b/Samples/Swift/AppAttestExample/AppAttestExample/Info.plist index b9179a51..e8f329f4 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample/Info.plist +++ b/Samples/Swift/AppAttestExample/AppAttestExample/Info.plist @@ -2,6 +2,8 @@ + APP_CHECK_WEB_API_KEY + $(APP_CHECK_WEB_API_KEY) CFBundleURLTypes From b0f7706992ace0b78f33dc88b2bd50e188d6a75a Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 13 Sep 2023 15:31:02 -0700 Subject: [PATCH 14/26] Add SPM sample project for App Check (#339) --- .github/workflows/integration_tests.yml | 32 ++ .gitignore | 1 - Package.swift | 2 +- .../project.pbxproj | 223 ++++++-- .../xcschemes/AppAttestExample.xcscheme | 22 +- .../AppAttestExample.xctestplan | 40 ++ .../AppCheckSecretReader.swift | 18 +- .../Secrets/AppCheckDefaultSecrets.xcconfig | 16 + .../project.pbxproj | 474 ++++++++++++++++++ .../xcschemes/AppAttestExample.xcscheme | 113 +++++ .../AppAttestExampleTests.swift | 30 ++ Samples/Swift/AppAttestExample/Podfile | 4 +- Samples/Swift/AppAttestExample/README.md | 60 +++ 13 files changed, 972 insertions(+), 63 deletions(-) create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample.xctestplan create mode 100644 Samples/Swift/AppAttestExample/AppAttestExample/Secrets/AppCheckDefaultSecrets.xcconfig create mode 100644 Samples/Swift/AppAttestExample/AppAttestExampleForPod.xcodeproj/project.pbxproj create mode 100644 Samples/Swift/AppAttestExample/AppAttestExampleForPod.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme create mode 100644 Samples/Swift/AppAttestExample/AppAttestExampleTests/AppAttestExampleTests.swift diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 08aee1c9..deb3d29e 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -40,3 +40,35 @@ jobs: -destination 'platform=iOS Simulator,name=iPhone 11' \ EMAIL_SECRET=$EMAIL_SECRET \ PASSWORD_SECRET=$PASSWORD_SECRET + + app-check-api-token-tests: + runs-on: macOS-12 + # Don't run if triggered by a PR from a fork since our Secrets won't be provided to the runner. + if: "!github.event.pull_request.head.repo.fork" + defaults: + run: + working-directory: Samples/Swift/AppAttestExample + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Build test target for App Check Example + run: | + xcodebuild \ + -project AppAttestExample.xcodeproj \ + build-for-testing \ + -scheme AppAttestExample \ + -sdk iphonesimulator \ + -destination 'platform=iOS Simulator,name=iPhone 11' + - name: Run test target for App Check Example + env: + AppCheckDebugToken : ${{ secrets.APP_CHECK_DEBUG_TOKEN }} + APP_CHECK_WEB_API_KEY : ${{ secrets.APP_CHECK_WEB_API_KEY }} + run: | + xcodebuild \ + -project AppAttestExample.xcodeproj \ + test-without-building \ + -scheme AppAttestExample \ + -sdk iphonesimulator \ + -destination 'platform=iOS Simulator,name=iPhone 11' \ + AppCheckDebugToken=$AppCheckDebugToken \ + APP_CHECK_WEB_API_KEY=$APP_CHECK_WEB_API_KEY diff --git a/.gitignore b/.gitignore index 103cb405..538419c3 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,3 @@ Podfile.lock # Firebase App Check Example **/GoogleService-Info.plist -**/AppCheckSecrets.xcconfig diff --git a/Package.swift b/Package.swift index 61590a4e..2b9011a6 100644 --- a/Package.swift +++ b/Package.swift @@ -48,7 +48,7 @@ let package = Package( .package( name: "AppCheck", url: "https://github.com/google/app-check.git", - .branch("CocoaPods-0.1.0-alpha.6")), + .branch("CocoaPods-0.1.0-alpha.9")), .package( name: "GTMAppAuth", url: "https://github.com/google/GTMAppAuth.git", diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj index e976444d..2309bd41 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj @@ -7,18 +7,32 @@ objects = { /* Begin PBXBuildFile section */ - 4D8DB53AAE2F7D0055DCEA7F /* Pods_AppAttestExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */; }; + 73080B2B2AAF9BDE00DEF667 /* AppAttestExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73080B2A2AAF9BDE00DEF667 /* AppAttestExampleTests.swift */; }; + 73080B392AAF9F1400DEF667 /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 73080B382AAF9F1400DEF667 /* GoogleSignIn */; }; 738B4A322AA8FE800056885D /* AppCheckSecretReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */; }; 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */; }; 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */; }; 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464052A1C3B3400BA8528 /* ContentView.swift */; }; 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */; }; + 73CD4AB22AAF915900642462 /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 73CD4AB12AAF915900642462 /* GoogleSignIn */; }; + 73CD4AB42AAF915F00642462 /* GoogleSignInSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 73CD4AB32AAF915F00642462 /* GoogleSignInSwift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 73080B352AAF9EAD00DEF667 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 73A463F82A1C3B3400BA8528 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 73A463FF2A1C3B3400BA8528; + remoteInfo = AppAttestExample; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ - 1C96B5B2B34E31F1A1CEE95E /* Pods-AppAttestExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttestExample.release.xcconfig"; path = "Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample.release.xcconfig"; sourceTree = ""; }; + 73080B282AAF9BDE00DEF667 /* AppAttestExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAttestExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 73080B2A2AAF9BDE00DEF667 /* AppAttestExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleTests.swift; sourceTree = ""; }; 73443A232A55F56900A4932E /* AppAttestExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppAttestExample.entitlements; sourceTree = ""; }; - 738B4A302AA7EB840056885D /* AppCheckSecrets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppCheckSecrets.xcconfig; sourceTree = ""; }; + 734555752AB167B80068F2B0 /* AppCheckDefaultSecrets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppCheckDefaultSecrets.xcconfig; sourceTree = ""; }; 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCheckSecretReader.swift; sourceTree = ""; }; 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = ""; }; 73A065612A786D10007BC7FC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -26,37 +40,56 @@ 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleApp.swift; sourceTree = ""; }; 73A464052A1C3B3400BA8528 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 7D9832F2FFAF408698660CA8 /* Pods-AppAttestExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttestExample.debug.xcconfig"; path = "Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample.debug.xcconfig"; sourceTree = ""; }; + 73CD4AB02AAF8C8500642462 /* GoogleSignIn-iOS */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "GoogleSignIn-iOS"; path = ../../..; sourceTree = ""; }; + 73D87D512AAFE0FE002D841C /* AppAttestExample.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = AppAttestExample.xctestplan; sourceTree = ""; }; 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppAttestExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 73080B252AAF9BDE00DEF667 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 73080B392AAF9F1400DEF667 /* GoogleSignIn in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 73A463FD2A1C3B3400BA8528 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4D8DB53AAE2F7D0055DCEA7F /* Pods_AppAttestExample.framework in Frameworks */, + 73CD4AB22AAF915900642462 /* GoogleSignIn in Frameworks */, + 73CD4AB42AAF915F00642462 /* GoogleSignInSwift in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 6B1005926777EEB3C903F93A /* Pods */ = { + 73080B292AAF9BDE00DEF667 /* AppAttestExampleTests */ = { isa = PBXGroup; children = ( - 7D9832F2FFAF408698660CA8 /* Pods-AppAttestExample.debug.xcconfig */, - 1C96B5B2B34E31F1A1CEE95E /* Pods-AppAttestExample.release.xcconfig */, + 73080B2A2AAF9BDE00DEF667 /* AppAttestExampleTests.swift */, ); - path = Pods; + path = AppAttestExampleTests; + sourceTree = ""; + }; + 7345556B2AB127B00068F2B0 /* Secrets */ = { + isa = PBXGroup; + children = ( + 734555752AB167B80068F2B0 /* AppCheckDefaultSecrets.xcconfig */, + ); + path = Secrets; sourceTree = ""; }; 73A463F72A1C3B3400BA8528 = { isa = PBXGroup; children = ( + 73D87D512AAFE0FE002D841C /* AppAttestExample.xctestplan */, + 73CD4AAF2AAF8C8500642462 /* Packages */, 73A464022A1C3B3400BA8528 /* AppAttestExample */, + 73080B292AAF9BDE00DEF667 /* AppAttestExampleTests */, 73A464012A1C3B3400BA8528 /* Products */, - 6B1005926777EEB3C903F93A /* Pods */, A73FBC2B93918F4B411815A1 /* Frameworks */, ); sourceTree = ""; @@ -65,6 +98,7 @@ isa = PBXGroup; children = ( 73A464002A1C3B3400BA8528 /* AppAttestExample.app */, + 73080B282AAF9BDE00DEF667 /* AppAttestExampleTests.xctest */, ); name = Products; sourceTree = ""; @@ -77,7 +111,7 @@ 73A464052A1C3B3400BA8528 /* ContentView.swift */, 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */, 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */, - 738B4A302AA7EB840056885D /* AppCheckSecrets.xcconfig */, + 7345556B2AB127B00068F2B0 /* Secrets */, 73A065612A786D10007BC7FC /* Info.plist */, 73A464092A1C3B3500BA8528 /* Preview Content */, ); @@ -92,6 +126,14 @@ path = "Preview Content"; sourceTree = ""; }; + 73CD4AAF2AAF8C8500642462 /* Packages */ = { + isa = PBXGroup; + children = ( + 73CD4AB02AAF8C8500642462 /* GoogleSignIn-iOS */, + ); + name = Packages; + sourceTree = ""; + }; A73FBC2B93918F4B411815A1 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -103,21 +145,44 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 73080B272AAF9BDE00DEF667 /* AppAttestExampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 73080B302AAF9BDE00DEF667 /* Build configuration list for PBXNativeTarget "AppAttestExampleTests" */; + buildPhases = ( + 73080B242AAF9BDE00DEF667 /* Sources */, + 73080B252AAF9BDE00DEF667 /* Frameworks */, + 73080B262AAF9BDE00DEF667 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 73080B362AAF9EAD00DEF667 /* PBXTargetDependency */, + ); + name = AppAttestExampleTests; + packageProductDependencies = ( + 73080B382AAF9F1400DEF667 /* GoogleSignIn */, + ); + productName = AppAttestExampleTests; + productReference = 73080B282AAF9BDE00DEF667 /* AppAttestExampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 73A463FF2A1C3B3400BA8528 /* AppAttestExample */ = { isa = PBXNativeTarget; buildConfigurationList = 73A4640E2A1C3B3500BA8528 /* Build configuration list for PBXNativeTarget "AppAttestExample" */; buildPhases = ( - D6AEC62E9810AEFD4C28F50F /* [CP] Check Pods Manifest.lock */, 73A463FC2A1C3B3400BA8528 /* Sources */, 73A463FD2A1C3B3400BA8528 /* Frameworks */, 73A463FE2A1C3B3400BA8528 /* Resources */, - C031D9D83F25CB0CD2512F23 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = AppAttestExample; + packageProductDependencies = ( + 73CD4AB12AAF915900642462 /* GoogleSignIn */, + 73CD4AB32AAF915F00642462 /* GoogleSignInSwift */, + ); productName = AppAttestExample; productReference = 73A464002A1C3B3400BA8528 /* AppAttestExample.app */; productType = "com.apple.product-type.application"; @@ -132,6 +197,10 @@ LastSwiftUpdateCheck = 1430; LastUpgradeCheck = 1430; TargetAttributes = { + 73080B272AAF9BDE00DEF667 = { + CreatedOnToolsVersion = 14.3; + TestTargetID = 73A463FF2A1C3B3400BA8528; + }; 73A463FF2A1C3B3400BA8528 = { CreatedOnToolsVersion = 14.3; }; @@ -151,64 +220,38 @@ projectRoot = ""; targets = ( 73A463FF2A1C3B3400BA8528 /* AppAttestExample */, + 73080B272AAF9BDE00DEF667 /* AppAttestExampleTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 73A463FE2A1C3B3400BA8528 /* Resources */ = { + 73080B262AAF9BDE00DEF667 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - C031D9D83F25CB0CD2512F23 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; + 73A463FE2A1C3B3400BA8528 /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample-resources-${CONFIGURATION}-output-files.xcfilelist", + 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample-resources.sh\"\n"; - showEnvVarsInLog = 0; }; - D6AEC62E9810AEFD4C28F50F /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 73080B242AAF9BDE00DEF667 /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-AppAttestExample-checkManifestLockResult.txt", + 73080B2B2AAF9BDE00DEF667 /* AppAttestExampleTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ 73A463FC2A1C3B3400BA8528 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -222,10 +265,54 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 73080B362AAF9EAD00DEF667 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 73A463FF2A1C3B3400BA8528 /* AppAttestExample */; + targetProxy = 73080B352AAF9EAD00DEF667 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ + 73080B2E2AAF9BDE00DEF667 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.AppAttestExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AppAttestExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AppAttestExample"; + }; + name = Debug; + }; + 73080B2F2AAF9BDE00DEF667 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.AppAttestExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AppAttestExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AppAttestExample"; + }; + name = Release; + }; 73A4640C2A1C3B3500BA8528 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 738B4A302AA7EB840056885D /* AppCheckSecrets.xcconfig */; + baseConfigurationReference = 734555752AB167B80068F2B0 /* AppCheckDefaultSecrets.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -275,7 +362,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -330,7 +417,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ""; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -342,7 +429,6 @@ }; 73A4640F2A1C3B3500BA8528 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7D9832F2FFAF408698660CA8 /* Pods-AppAttestExample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -355,6 +441,7 @@ DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; ENABLE_PREVIEWS = YES; + ENABLE_TESTING_SEARCH_PATHS = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AppAttestExample/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -362,6 +449,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -379,7 +467,6 @@ }; 73A464102A1C3B3500BA8528 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1C96B5B2B34E31F1A1CEE95E /* Pods-AppAttestExample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -392,6 +479,7 @@ DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; ENABLE_PREVIEWS = YES; + ENABLE_TESTING_SEARCH_PATHS = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AppAttestExample/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -399,6 +487,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -417,6 +506,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 73080B302AAF9BDE00DEF667 /* Build configuration list for PBXNativeTarget "AppAttestExampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 73080B2E2AAF9BDE00DEF667 /* Debug */, + 73080B2F2AAF9BDE00DEF667 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 73A463FB2A1C3B3400BA8528 /* Build configuration list for PBXProject "AppAttestExample" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -436,6 +534,21 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + 73080B382AAF9F1400DEF667 /* GoogleSignIn */ = { + isa = XCSwiftPackageProductDependency; + productName = GoogleSignIn; + }; + 73CD4AB12AAF915900642462 /* GoogleSignIn */ = { + isa = XCSwiftPackageProductDependency; + productName = GoogleSignIn; + }; + 73CD4AB32AAF915F00642462 /* GoogleSignInSwift */ = { + isa = XCSwiftPackageProductDependency; + productName = GoogleSignInSwift; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 73A463F82A1C3B3400BA8528 /* Project object */; } diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme index f2c125f8..d6cfaf2a 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme @@ -26,8 +26,26 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - shouldAutocreateTestPlan = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + + + + + + /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + D116F83514234BBFA76A4CC4 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-AppAttestExampleForPod/Pods-AppAttestExampleForPod-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-AppAttestExampleForPod/Pods-AppAttestExampleForPod-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AppAttestExampleForPod/Pods-AppAttestExampleForPod-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 73A463FC2A1C3B3400BA8528 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */, + 738B4A322AA8FE800056885D /* AppCheckSecretReader.swift in Sources */, + 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */, + 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 73A4640C2A1C3B3500BA8528 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 73A4640D2A1C3B3500BA8528 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 73A4640F2A1C3B3500BA8528 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E27EB8376FDCD7AB29F8A522 /* Pods-AppAttestExampleForPod.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AppAttestExample/AppAttestExample.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; + ENABLE_PREVIEWS = YES; + ENABLE_TESTING_SEARCH_PATHS = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AppAttestExample/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Experimental App 0 Dev"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 73A464102A1C3B3500BA8528 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DB9DDDAE875580597968F796 /* Pods-AppAttestExampleForPod.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AppAttestExample/AppAttestExample.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; + ENABLE_PREVIEWS = YES; + ENABLE_TESTING_SEARCH_PATHS = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AppAttestExample/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Experimental App 0 Dev"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 73A463FB2A1C3B3400BA8528 /* Build configuration list for PBXProject "AppAttestExampleForPod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 73A4640C2A1C3B3500BA8528 /* Debug */, + 73A4640D2A1C3B3500BA8528 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 73A4640E2A1C3B3500BA8528 /* Build configuration list for PBXNativeTarget "AppAttestExampleForPod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 73A4640F2A1C3B3500BA8528 /* Debug */, + 73A464102A1C3B3500BA8528 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 73A463F82A1C3B3400BA8528 /* Project object */; +} diff --git a/Samples/Swift/AppAttestExample/AppAttestExampleForPod.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme b/Samples/Swift/AppAttestExample/AppAttestExampleForPod.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme new file mode 100644 index 00000000..0dd1c353 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExampleForPod.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/Swift/AppAttestExample/AppAttestExampleTests/AppAttestExampleTests.swift b/Samples/Swift/AppAttestExample/AppAttestExampleTests/AppAttestExampleTests.swift new file mode 100644 index 00000000..ebdee3a4 --- /dev/null +++ b/Samples/Swift/AppAttestExample/AppAttestExampleTests/AppAttestExampleTests.swift @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import XCTest +@testable import AppAttestExample + +final class AppAttestExampleTests: XCTestCase { + func testThatAPIKeyCanBeReadFromBundleOrEnvironment() { + let secretsReader = AppCheckSecretReader() + XCTAssertNotNil(secretsReader.APIKey) + } + + func testThatDebugTokenCanBeReadFromEnvironment() { + let secretsReader = AppCheckSecretReader() + XCTAssertNotNil(secretsReader.debugToken) + } +} diff --git a/Samples/Swift/AppAttestExample/Podfile b/Samples/Swift/AppAttestExample/Podfile index 9554395e..2805b5c8 100644 --- a/Samples/Swift/AppAttestExample/Podfile +++ b/Samples/Swift/AppAttestExample/Podfile @@ -3,11 +3,11 @@ source 'https://github.com/firebase/SpecsDev.git' pod 'GoogleSignIn', :path => '../../../', :testspecs => ['unit'] pod 'GoogleSignInSwiftSupport', :path => '../../../', :testspecs => ['unit'] -project 'AppAttestExample.xcodeproj' +project 'AppAttestExampleForPod.xcodeproj' use_frameworks! :linkage => :static -target 'AppAttestExample' do +target 'AppAttestExampleForPod' do pod 'AppCheckCore' platform :ios, '14.0' end diff --git a/Samples/Swift/AppAttestExample/README.md b/Samples/Swift/AppAttestExample/README.md index e69de29b..a3b53404 100644 --- a/Samples/Swift/AppAttestExample/README.md +++ b/Samples/Swift/AppAttestExample/README.md @@ -0,0 +1,60 @@ +# Google Sign-In with Firebase App Check Sample App + +## CocoaPods + +1. In the `../Samples/Swift/AppAttestExample/` folder, run the following +[CocoaPods](https://cocoapods.org) command. + +``` +pod install +``` + +2. Open the generated workspace: + +``` +open AppAttestExample.xcworkspace +``` + +3. Run the `AppAttestExampleForPod` target. + +## Swift Package Manager + +1. In the `../Samples/Swift/AppAttestExample/` folder, open the project: + +``` +open AppAttestExample.xcodeproj +``` +2. Run the `AppAttestExample` target. + +## A Note on Provisioning Profiles + +You will need a provisioning profile with the App Attest entitlement. + +## Hiding Secrets + +This example app shows how you might hide your web API key and debug token +(used during CI; AppCheckCore manages the debug token running locally in the +simulator). Both of these are required. Inside the `Secrets/` directory, we +include a placeholder file entitled `AppCheckDefaultSecrets.xcconfig`. We have +also set that as a configuration file for the project, which means that it will +be used to find the web API key during debug builds on the simulator (for +example). You can either make a new file to fill in the stubbed data in +`AppCheckDefaultSecrets.xcconfig` (which will require that you update where the +projects finds its configurations), or you can add your API key there yourself. +Do make sure that you do not commit this API key, or you will risk exposing +this information on your repository. + +In builds running under continuous integration, make sure to use environment +variables and `AppCheckSecretReader.swift` will find your web API key and debug +token if you provide them to your `xcodebuild` command. + +## Integration Tests + +We show how you might hide your app's web API key and debug token when +running locally and in CI environments. See GitHub's +[secrets](https://docs.github.com/en/actions/learn-github-actions/contexts#secrets-context) +documentation for how you might set those values in your repo. + +Locally, both the web API key and the debug token need to be passed to +`xcodebuild` as arguments: +`xcodebuild APP_CHECK_WEB_API_KEY=... AppCheckDebugToken=...`. From c885e9bd2e707f2f7b4f5f3e706765711791b5a9 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 15 Sep 2023 11:55:53 -0700 Subject: [PATCH 15/26] Do not modally animate the presenting of app check loading UI (#340) --- .../UI/GIDActivityIndicatorViewController.m | 3 +++ .../Sources/GIDTimedLoader/GIDTimedLoader.m | 3 +++ .../project.pbxproj | 13 +++++-------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m index fa9888e6..b3f34db5 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m +++ b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m @@ -24,6 +24,8 @@ @implementation GIDActivityIndicatorViewController - (void)viewDidLoad { [super viewDidLoad]; + // Medium gray with transparency + self.view.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:0.25]; UIActivityIndicatorViewStyle style; if (@available(iOS 13.0, *)) { @@ -32,6 +34,7 @@ - (void)viewDidLoad { style = UIActivityIndicatorViewStyleGray; } _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:style]; + _activityIndicator.color = UIColor.whiteColor; self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO; [self.activityIndicator startAnimating]; [self.view addSubview:self.activityIndicator]; diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m index c009dccf..02bad80d 100644 --- a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m @@ -68,6 +68,9 @@ - (void)presentLoadingViewController { self.loadingTimeStamp = CACurrentMediaTime(); dispatch_async(dispatch_get_main_queue(), ^{ // Since this loading VC may be reused, the activity indicator may have been stopped; restart it + self.loadingViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext; + self.loadingViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + self.presentingViewController.definesPresentationContext = YES; [self.loadingViewController.activityIndicator startAnimating]; [self.presentingViewController presentViewController:self.loadingViewController animated:YES diff --git a/Samples/Swift/AppAttestExample/AppAttestExampleForPod.xcodeproj/project.pbxproj b/Samples/Swift/AppAttestExample/AppAttestExampleForPod.xcodeproj/project.pbxproj index a951a7e8..bfbccbf2 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExampleForPod.xcodeproj/project.pbxproj +++ b/Samples/Swift/AppAttestExample/AppAttestExampleForPod.xcodeproj/project.pbxproj @@ -7,8 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 7345556F2AB142B40068F2B0 /* AppCheckDefaultSecrets.json in Resources */ = {isa = PBXBuildFile; fileRef = 7345556D2AB142B40068F2B0 /* AppCheckDefaultSecrets.json */; }; - 734555702AB142B40068F2B0 /* AppCheckSecrets.json in Resources */ = {isa = PBXBuildFile; fileRef = 7345556E2AB142B40068F2B0 /* AppCheckSecrets.json */; }; + 734555A02AB39FBF0068F2B0 /* AppCheckDefaultSecrets.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 7345559F2AB39FBF0068F2B0 /* AppCheckDefaultSecrets.xcconfig */; }; 738B4A322AA8FE800056885D /* AppCheckSecretReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */; }; 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */; }; 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */; }; @@ -22,8 +21,7 @@ 4728D878D216B7D622E237DA /* Pods_AppAttestExampleForPod.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppAttestExampleForPod.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 73080B2A2AAF9BDE00DEF667 /* AppAttestExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleTests.swift; sourceTree = ""; }; 73443A232A55F56900A4932E /* AppAttestExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppAttestExample.entitlements; sourceTree = ""; }; - 7345556D2AB142B40068F2B0 /* AppCheckDefaultSecrets.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = AppCheckDefaultSecrets.json; sourceTree = ""; }; - 7345556E2AB142B40068F2B0 /* AppCheckSecrets.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = AppCheckSecrets.json; sourceTree = ""; }; + 7345559F2AB39FBF0068F2B0 /* AppCheckDefaultSecrets.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = AppCheckDefaultSecrets.xcconfig; sourceTree = ""; }; 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCheckSecretReader.swift; sourceTree = ""; }; 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = ""; }; 73A065612A786D10007BC7FC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -71,8 +69,7 @@ 7345556C2AB142B40068F2B0 /* Secrets */ = { isa = PBXGroup; children = ( - 7345556D2AB142B40068F2B0 /* AppCheckDefaultSecrets.json */, - 7345556E2AB142B40068F2B0 /* AppCheckSecrets.json */, + 7345559F2AB39FBF0068F2B0 /* AppCheckDefaultSecrets.xcconfig */, ); path = Secrets; sourceTree = ""; @@ -188,9 +185,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7345556F2AB142B40068F2B0 /* AppCheckDefaultSecrets.json in Resources */, 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */, - 734555702AB142B40068F2B0 /* AppCheckSecrets.json in Resources */, + 734555A02AB39FBF0068F2B0 /* AppCheckDefaultSecrets.xcconfig in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -255,6 +251,7 @@ /* Begin XCBuildConfiguration section */ 73A4640C2A1C3B3500BA8528 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 7345559F2AB39FBF0068F2B0 /* AppCheckDefaultSecrets.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; From 230a2f274014517ff545fb89d2dd5f12893706a9 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Mon, 16 Oct 2023 16:26:45 -0700 Subject: [PATCH 16/26] Remove unnecessary TODO (#343) --- GoogleSignIn/Sources/GIDGoogleUser.m | 1 - 1 file changed, 1 deletion(-) diff --git a/GoogleSignIn/Sources/GIDGoogleUser.m b/GoogleSignIn/Sources/GIDGoogleUser.m index 817f57e1..ec300839 100644 --- a/GoogleSignIn/Sources/GIDGoogleUser.m +++ b/GoogleSignIn/Sources/GIDGoogleUser.m @@ -117,7 +117,6 @@ - (GIDConfiguration *)configuration { return _cachedConfiguration; } -// TODO: Should the refresh tokens flow also use App Check? (mdmathias, 2023.05.23) - (void)refreshTokensIfNeededWithCompletion:(GIDGoogleUserCompletion)completion { if (!([self.accessToken.expirationDate timeIntervalSinceNow] < kMinimalTimeToExpire || (self.idToken && [self.idToken.expirationDate timeIntervalSinceNow] < kMinimalTimeToExpire))) { From e1d6b0a10d7ad7c7470d3ecdfeadbcdb5632c219 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 3 Nov 2023 10:15:53 -0700 Subject: [PATCH 17/26] Increase the max animation delay for App Check (#347) --- GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m index 02bad80d..28bab351 100644 --- a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m @@ -24,7 +24,7 @@ #import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" CFTimeInterval const kGIDTimedLoaderMinAnimationDuration = 1.0; -CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating = 0.5; +CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating = 0.8; @interface GIDTimedLoader () From c39641d50b00e431f0ac6886d13857044fa6b27f Mon Sep 17 00:00:00 2001 From: mdmathias Date: Tue, 14 Nov 2023 17:32:42 -0800 Subject: [PATCH 18/26] Update AppCheckCore dependency to 10.18 (#350) --- .github/workflows/unit_tests.yml | 2 +- GoogleSignIn.podspec | 2 +- Package.swift | 2 +- Samples/Swift/AppAttestExample/Podfile | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index f7cddc21..ddeaf844 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -32,7 +32,7 @@ jobs: - name: Lint podspec using local source run: | pod lib lint ${{ matrix.podspec }} --verbose \ - --sources=https://github.com/firebase/SpecsDev.git,https://cdn.cocoapods.org/ \ + --sources=https://cdn.cocoapods.org/ \ ${{ matrix.includePodspecFlag }} ${{ matrix.flag }} spm-build-test: diff --git a/GoogleSignIn.podspec b/GoogleSignIn.podspec index ffdce777..67d3f4af 100644 --- a/GoogleSignIn.podspec +++ b/GoogleSignIn.podspec @@ -33,7 +33,7 @@ The Google Sign-In SDK allows users to sign in with their Google account from th ] s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' - s.dependency 'AppCheckCore', '~> 0.1.0-alpha.9' + s.dependency 'AppCheckCore', '~> 10.18' s.dependency 'AppAuth', '>= 1.7.3', '< 2.0' s.dependency 'GTMAppAuth', '>= 4.1.1', '< 5.0' s.dependency 'GTMSessionFetcher/Core', '~> 3.3' diff --git a/Package.swift b/Package.swift index 2b9011a6..48cfea6b 100644 --- a/Package.swift +++ b/Package.swift @@ -48,7 +48,7 @@ let package = Package( .package( name: "AppCheck", url: "https://github.com/google/app-check.git", - .branch("CocoaPods-0.1.0-alpha.9")), + "10.18.0" ..< "11.0.0"), .package( name: "GTMAppAuth", url: "https://github.com/google/GTMAppAuth.git", diff --git a/Samples/Swift/AppAttestExample/Podfile b/Samples/Swift/AppAttestExample/Podfile index 2805b5c8..ebd1d406 100644 --- a/Samples/Swift/AppAttestExample/Podfile +++ b/Samples/Swift/AppAttestExample/Podfile @@ -1,5 +1,4 @@ source 'https://github.com/CocoaPods/Specs.git' -source 'https://github.com/firebase/SpecsDev.git' pod 'GoogleSignIn', :path => '../../../', :testspecs => ['unit'] pod 'GoogleSignInSwiftSupport', :path => '../../../', :testspecs => ['unit'] From 1a5eee4ec461aa9236278ca4bed642620e042811 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Mon, 20 Nov 2023 14:06:27 -0800 Subject: [PATCH 19/26] Prepare for Firebase App Check EAP release (#352) --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f46fa6e8..f76a05d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,25 @@ - Removes `macos-11` runner in GitHub workflows ([#302](https://github.com/google/GoogleSignIn-iOS/pull/302)) - Updates button name reference so UI automation tests pass ([#308](https://github.com/google/GoogleSignIn-iOS/pull/308)) +# 7.1.0-fac-eap-1.0.0 +- Early Access Program (EAP) release supporting Firebase App Check tokens used +to establish your application's integrity while signing in with Google + - Use [`-[GIDSignIn configureWithCompletion:]`](https://github.com/google/GoogleSignIn-iOS/blob/7.1.0-fac-eap-1.0.0/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h#L79) + to configure GSI to use Firebase App Check as early as possible in your app + to minimize latency. + - Use [`-[GIDSignIn configureDebugProviderWithAPIKey:completion:]`](https://github.com/google/GoogleSignIn-iOS/blob/7.1.0-fac-eap-1.0.0/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h#L91) + in debug builds or continuous integration environments. + - New [sample app](https://github.com/google/GoogleSignIn-iOS/tree/7.1.0-fac-eap-1.0.0/Samples/Swift/AppAttestExample) + showing example of configuring GSI to use Firebase App Check. +- Internal + - Fix typo in `SFSafariViewController` ([#291](https://github.com/google/GoogleSignIn-iOS/pull/291)) + - Removes `macos-11` runner in GitHub workflows ([#302](https://github.com/google/GoogleSignIn-iOS/pull/302)) + - Updates button name reference so UI automation tests pass ([#308](https://github.com/google/GoogleSignIn-iOS/pull/308)) + - Ensure that `completion` is not nil before calling + `-[GIDSignIn restorePreviousSignIn:]` ([#301](https://github.com/google/GoogleSignIn-iOS/pull/301)) + - Use new [delegate protocol](https://github.com/google/GTMAppAuth/pull/224) + from GTMAppAuth 4.0.0 ([#299](https://github.com/google/GoogleSignIn-iOS/pull/299)) + # 7.0.0 - All configuration can now be provided via your `Info.plist` file. ([#228](https://github.com/google/GoogleSignIn-iOS/pull/228)) - Use the following keys in `KEYVALUE` pairs to configure the SDK: From 677d8ccf6b6c49126046288cd2f2af4a1c3111f9 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Thu, 7 Dec 2023 16:06:04 -0800 Subject: [PATCH 20/26] Update SignInSample Podfile minimum iOS version (#355) --- Samples/ObjC/SignInSample/Podfile | 2 +- .../SignInSampleForPod.xcodeproj/project.pbxproj | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Samples/ObjC/SignInSample/Podfile b/Samples/ObjC/SignInSample/Podfile index 003fefe1..d08d7b41 100644 --- a/Samples/ObjC/SignInSample/Podfile +++ b/Samples/ObjC/SignInSample/Podfile @@ -1,4 +1,4 @@ -platform :ios, '10.0' +platform :ios, '11.0' use_frameworks! target 'SampleForPod' do diff --git a/Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj b/Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj index 417d1c30..ce6f4817 100644 --- a/Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj +++ b/Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj @@ -308,16 +308,22 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-SampleForPod/Pods-SampleForPod-frameworks.sh", "${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework", + "${BUILT_PRODUCTS_DIR}/AppCheckCore/AppCheckCore.framework", "${BUILT_PRODUCTS_DIR}/GTMAppAuth/GTMAppAuth.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/GoogleSignIn/GoogleSignIn.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppCheckCore.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMAppAuth.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleSignIn.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; From f53b88baf4dcd98f872fc3f319ce260d67914114 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 20 Dec 2023 09:31:45 -0800 Subject: [PATCH 21/26] =?UTF-8?q?Update=20AppCheckExample=20unit=20test=20?= =?UTF-8?q?target=20to=20pass=20during=20continuous=20int=E2=80=A6=20(#356?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration_tests.yml | 9 +- .../project.pbxproj | 136 ++++++++++-------- .../xcschemes/AppAttestExample.xcscheme | 13 +- .../AppAttestExample.xctestplan | 24 ++-- 4 files changed, 107 insertions(+), 75 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index deb3d29e..92ae37eb 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -42,7 +42,7 @@ jobs: PASSWORD_SECRET=$PASSWORD_SECRET app-check-api-token-tests: - runs-on: macOS-12 + runs-on: macOS-13 # Don't run if triggered by a PR from a fork since our Secrets won't be provided to the runner. if: "!github.event.pull_request.head.repo.fork" defaults: @@ -51,6 +51,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + - name: Use Xcode 15.0.1 + run: | + sudo xcode-select -s /Applications/Xcode_15.0.1.app - name: Build test target for App Check Example run: | xcodebuild \ @@ -58,7 +61,7 @@ jobs: build-for-testing \ -scheme AppAttestExample \ -sdk iphonesimulator \ - -destination 'platform=iOS Simulator,name=iPhone 11' + -destination 'platform=iOS Simulator,name=iPhone 15' - name: Run test target for App Check Example env: AppCheckDebugToken : ${{ secrets.APP_CHECK_DEBUG_TOKEN }} @@ -69,6 +72,6 @@ jobs: test-without-building \ -scheme AppAttestExample \ -sdk iphonesimulator \ - -destination 'platform=iOS Simulator,name=iPhone 11' \ + -destination 'platform=iOS Simulator,name=iPhone 15' \ AppCheckDebugToken=$AppCheckDebugToken \ APP_CHECK_WEB_API_KEY=$APP_CHECK_WEB_API_KEY diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj index 2309bd41..8f3a188f 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj @@ -3,12 +3,11 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ - 73080B2B2AAF9BDE00DEF667 /* AppAttestExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73080B2A2AAF9BDE00DEF667 /* AppAttestExampleTests.swift */; }; - 73080B392AAF9F1400DEF667 /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 73080B382AAF9F1400DEF667 /* GoogleSignIn */; }; + 736434232B2A914B00DA67DA /* AppAttestExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736434222B2A914B00DA67DA /* AppAttestExampleTests.swift */; }; 738B4A322AA8FE800056885D /* AppCheckSecretReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */; }; 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */; }; 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */; }; @@ -19,7 +18,7 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 73080B352AAF9EAD00DEF667 /* PBXContainerItemProxy */ = { + 736434242B2A914B00DA67DA /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 73A463F82A1C3B3400BA8528 /* Project object */; proxyType = 1; @@ -29,10 +28,11 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 73080B282AAF9BDE00DEF667 /* AppAttestExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAttestExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 73080B2A2AAF9BDE00DEF667 /* AppAttestExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleTests.swift; sourceTree = ""; }; 73443A232A55F56900A4932E /* AppAttestExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppAttestExample.entitlements; sourceTree = ""; }; 734555752AB167B80068F2B0 /* AppCheckDefaultSecrets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppCheckDefaultSecrets.xcconfig; sourceTree = ""; }; + 736434202B2A914A00DA67DA /* AppAttestExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAttestExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 736434222B2A914B00DA67DA /* AppAttestExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleTests.swift; sourceTree = ""; }; + 736434292B2A923C00DA67DA /* AppAttestExample.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = AppAttestExample.xctestplan; sourceTree = SOURCE_ROOT; }; 738B4A312AA8FE800056885D /* AppCheckSecretReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCheckSecretReader.swift; sourceTree = ""; }; 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = ""; }; 73A065612A786D10007BC7FC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -40,17 +40,15 @@ 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleApp.swift; sourceTree = ""; }; 73A464052A1C3B3400BA8528 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 73CD4AB02AAF8C8500642462 /* GoogleSignIn-iOS */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "GoogleSignIn-iOS"; path = ../../..; sourceTree = ""; }; - 73D87D512AAFE0FE002D841C /* AppAttestExample.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = AppAttestExample.xctestplan; sourceTree = ""; }; + 73DB85BB2B23DAEC00D051BA /* CoreAudioTypes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioTypes.framework; path = System/Library/Frameworks/CoreAudioTypes.framework; sourceTree = SDKROOT; }; 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppAttestExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 73080B252AAF9BDE00DEF667 /* Frameworks */ = { + 7364341D2B2A914A00DA67DA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 73080B392AAF9F1400DEF667 /* GoogleSignIn in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -66,29 +64,28 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 73080B292AAF9BDE00DEF667 /* AppAttestExampleTests */ = { + 7345556B2AB127B00068F2B0 /* Secrets */ = { isa = PBXGroup; children = ( - 73080B2A2AAF9BDE00DEF667 /* AppAttestExampleTests.swift */, + 734555752AB167B80068F2B0 /* AppCheckDefaultSecrets.xcconfig */, ); - path = AppAttestExampleTests; + path = Secrets; sourceTree = ""; }; - 7345556B2AB127B00068F2B0 /* Secrets */ = { + 736434212B2A914B00DA67DA /* AppAttestExampleTests */ = { isa = PBXGroup; children = ( - 734555752AB167B80068F2B0 /* AppCheckDefaultSecrets.xcconfig */, + 736434222B2A914B00DA67DA /* AppAttestExampleTests.swift */, + 736434292B2A923C00DA67DA /* AppAttestExample.xctestplan */, ); - path = Secrets; + path = AppAttestExampleTests; sourceTree = ""; }; 73A463F72A1C3B3400BA8528 = { isa = PBXGroup; children = ( - 73D87D512AAFE0FE002D841C /* AppAttestExample.xctestplan */, - 73CD4AAF2AAF8C8500642462 /* Packages */, 73A464022A1C3B3400BA8528 /* AppAttestExample */, - 73080B292AAF9BDE00DEF667 /* AppAttestExampleTests */, + 736434212B2A914B00DA67DA /* AppAttestExampleTests */, 73A464012A1C3B3400BA8528 /* Products */, A73FBC2B93918F4B411815A1 /* Frameworks */, ); @@ -98,7 +95,7 @@ isa = PBXGroup; children = ( 73A464002A1C3B3400BA8528 /* AppAttestExample.app */, - 73080B282AAF9BDE00DEF667 /* AppAttestExampleTests.xctest */, + 736434202B2A914A00DA67DA /* AppAttestExampleTests.xctest */, ); name = Products; sourceTree = ""; @@ -126,17 +123,10 @@ path = "Preview Content"; sourceTree = ""; }; - 73CD4AAF2AAF8C8500642462 /* Packages */ = { - isa = PBXGroup; - children = ( - 73CD4AB02AAF8C8500642462 /* GoogleSignIn-iOS */, - ); - name = Packages; - sourceTree = ""; - }; A73FBC2B93918F4B411815A1 /* Frameworks */ = { isa = PBXGroup; children = ( + 73DB85BB2B23DAEC00D051BA /* CoreAudioTypes.framework */, 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */, ); name = Frameworks; @@ -145,25 +135,24 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 73080B272AAF9BDE00DEF667 /* AppAttestExampleTests */ = { + 7364341F2B2A914A00DA67DA /* AppAttestExampleTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 73080B302AAF9BDE00DEF667 /* Build configuration list for PBXNativeTarget "AppAttestExampleTests" */; + buildConfigurationList = 736434262B2A914B00DA67DA /* Build configuration list for PBXNativeTarget "AppAttestExampleTests" */; buildPhases = ( - 73080B242AAF9BDE00DEF667 /* Sources */, - 73080B252AAF9BDE00DEF667 /* Frameworks */, - 73080B262AAF9BDE00DEF667 /* Resources */, + 7364341C2B2A914A00DA67DA /* Sources */, + 7364341D2B2A914A00DA67DA /* Frameworks */, + 7364341E2B2A914A00DA67DA /* Resources */, ); buildRules = ( ); dependencies = ( - 73080B362AAF9EAD00DEF667 /* PBXTargetDependency */, + 736434252B2A914B00DA67DA /* PBXTargetDependency */, ); name = AppAttestExampleTests; packageProductDependencies = ( - 73080B382AAF9F1400DEF667 /* GoogleSignIn */, ); productName = AppAttestExampleTests; - productReference = 73080B282AAF9BDE00DEF667 /* AppAttestExampleTests.xctest */; + productReference = 736434202B2A914A00DA67DA /* AppAttestExampleTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 73A463FF2A1C3B3400BA8528 /* AppAttestExample */ = { @@ -194,11 +183,11 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1430; + LastSwiftUpdateCheck = 1500; LastUpgradeCheck = 1430; TargetAttributes = { - 73080B272AAF9BDE00DEF667 = { - CreatedOnToolsVersion = 14.3; + 7364341F2B2A914A00DA67DA = { + CreatedOnToolsVersion = 15.0.1; TestTargetID = 73A463FF2A1C3B3400BA8528; }; 73A463FF2A1C3B3400BA8528 = { @@ -215,18 +204,21 @@ Base, ); mainGroup = 73A463F72A1C3B3400BA8528; + packageReferences = ( + 73A0EE262B2BD381001595C9 /* XCLocalSwiftPackageReference "../../.." */, + ); productRefGroup = 73A464012A1C3B3400BA8528 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 73A463FF2A1C3B3400BA8528 /* AppAttestExample */, - 73080B272AAF9BDE00DEF667 /* AppAttestExampleTests */, + 7364341F2B2A914A00DA67DA /* AppAttestExampleTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 73080B262AAF9BDE00DEF667 /* Resources */ = { + 7364341E2B2A914A00DA67DA /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -244,11 +236,11 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 73080B242AAF9BDE00DEF667 /* Sources */ = { + 7364341C2B2A914A00DA67DA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 73080B2B2AAF9BDE00DEF667 /* AppAttestExampleTests.swift in Sources */, + 736434232B2A914B00DA67DA /* AppAttestExampleTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -266,25 +258,31 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 73080B362AAF9EAD00DEF667 /* PBXTargetDependency */ = { + 736434252B2A914B00DA67DA /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 73A463FF2A1C3B3400BA8528 /* AppAttestExample */; - targetProxy = 73080B352AAF9EAD00DEF667 /* PBXContainerItemProxy */; + targetProxy = 736434242B2A914B00DA67DA /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 73080B2E2AAF9BDE00DEF667 /* Debug */ = { + 736434272B2A914B00DA67DA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.google.AppAttestExampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -292,15 +290,20 @@ }; name = Debug; }; - 73080B2F2AAF9BDE00DEF667 /* Release */ = { + 736434282B2A914B00DA67DA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.google.AppAttestExampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -312,7 +315,6 @@ }; 73A4640C2A1C3B3500BA8528 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 734555752AB167B80068F2B0 /* AppCheckDefaultSecrets.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -347,6 +349,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ""; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -366,6 +369,7 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ""; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -408,6 +412,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ""; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -420,6 +425,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ""; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; @@ -429,6 +435,7 @@ }; 73A4640F2A1C3B3500BA8528 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 734555752AB167B80068F2B0 /* AppCheckDefaultSecrets.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -437,7 +444,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; + DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; ENABLE_PREVIEWS = YES; @@ -455,10 +462,14 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Experimental App 0 Dev"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -475,7 +486,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; + DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; ENABLE_PREVIEWS = YES; @@ -493,10 +504,14 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Experimental App 0 Dev"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -506,11 +521,11 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 73080B302AAF9BDE00DEF667 /* Build configuration list for PBXNativeTarget "AppAttestExampleTests" */ = { + 736434262B2A914B00DA67DA /* Build configuration list for PBXNativeTarget "AppAttestExampleTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 73080B2E2AAF9BDE00DEF667 /* Debug */, - 73080B2F2AAF9BDE00DEF667 /* Release */, + 736434272B2A914B00DA67DA /* Debug */, + 736434282B2A914B00DA67DA /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -535,11 +550,14 @@ }; /* End XCConfigurationList section */ -/* Begin XCSwiftPackageProductDependency section */ - 73080B382AAF9F1400DEF667 /* GoogleSignIn */ = { - isa = XCSwiftPackageProductDependency; - productName = GoogleSignIn; +/* Begin XCLocalSwiftPackageReference section */ + 73A0EE262B2BD381001595C9 /* XCLocalSwiftPackageReference "../../.." */ = { + isa = XCLocalSwiftPackageReference; + relativePath = ../../..; }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ 73CD4AB12AAF915900642462 /* GoogleSignIn */ = { isa = XCSwiftPackageProductDependency; productName = GoogleSignIn; diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme index d6cfaf2a..5679a777 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/xcshareddata/xcschemes/AppAttestExample.xcscheme @@ -39,7 +39,18 @@ parallelizable = "YES"> + + + + diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xctestplan b/Samples/Swift/AppAttestExample/AppAttestExample.xctestplan index 1f7415fb..74185c6d 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xctestplan +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xctestplan @@ -1,15 +1,21 @@ { "configurations" : [ { - "id" : "BE893D88-5ECF-4AF1-95E1-CE186B02A525", - "name" : "Test Scheme Action", + "id" : "CBD3C208-A37F-4B73-9881-D0EBF17004C2", + "name" : "Configuration 1", "options" : { - + "targetForVariableExpansion" : { + "containerPath" : "container:AppAttestExample.xcodeproj", + "identifier" : "7364341F2B2A914A00DA67DA", + "name" : "AppAttestExampleTests" + } } } ], "defaultOptions" : { - "codeCoverage" : false, + "commandLineArgumentEntries" : [ + + ], "environmentVariableEntries" : [ { "key" : "AppCheckDebugToken", @@ -19,19 +25,13 @@ "key" : "APP_CHECK_WEB_API_KEY", "value" : "$(APP_CHECK_WEB_API_KEY)" } - ], - "targetForVariableExpansion" : { - "containerPath" : "container:AppAttestExample.xcodeproj", - "identifier" : "73080B272AAF9BDE00DEF667", - "name" : "AppAttestExampleTests" - } + ] }, "testTargets" : [ { - "parallelizable" : true, "target" : { "containerPath" : "container:AppAttestExample.xcodeproj", - "identifier" : "73080B272AAF9BDE00DEF667", + "identifier" : "7364341F2B2A914A00DA67DA", "name" : "AppAttestExampleTests" } } From aadbc595a6efc62cc857b20eace0bedf1c04b616 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Fri, 9 Feb 2024 20:58:13 -0800 Subject: [PATCH 22/26] Prepare for AppCheck beta release (#366) --- .github/workflows/integration_tests.yml | 2 +- CHANGELOG.md | 7 +++++++ .../SignInSample/SignInSample.xcodeproj/project.pbxproj | 4 +++- .../SignInSampleForPod.xcodeproj/project.pbxproj | 4 ++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 92ae37eb..0a925f61 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -12,7 +12,7 @@ jobs: swift-button-functional-test: runs-on: macOS-12 # Don't run if triggered by a PR from a fork since our Secrets won't be provided to the runner. - if: "!github.event.pull_request.head.repo.fork" + if: false # Disabled per issue 367; add back here check for PRs from forks defaults: run: working-directory: Samples/Swift/DaysUntilBirthday diff --git a/CHANGELOG.md b/CHANGELOG.md index f76a05d4..218a9222 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,13 @@ - Removes `macos-11` runner in GitHub workflows ([#302](https://github.com/google/GoogleSignIn-iOS/pull/302)) - Updates button name reference so UI automation tests pass ([#308](https://github.com/google/GoogleSignIn-iOS/pull/308)) +# 7.1.0-fac-beta-1.0.0 +- Beta release supporting Firebase App Check tokens used +to establish your application's integrity while signing in with Google +- Internal + - Update SignInSample Podfile minimum iOS version ([#355](https://github.com/google/GoogleSignIn-iOS/pull/355)) + - Update AppCheckExample unit test target to pass during continuous integration ([#356](https://github.com/google/GoogleSignIn-iOS/pull/356)) + # 7.1.0-fac-eap-1.0.0 - Early Access Program (EAP) release supporting Firebase App Check tokens used to establish your application's integrity while signing in with Google diff --git a/Samples/ObjC/SignInSample/SignInSample.xcodeproj/project.pbxproj b/Samples/ObjC/SignInSample/SignInSample.xcodeproj/project.pbxproj index cff9da77..0ffd550e 100644 --- a/Samples/ObjC/SignInSample/SignInSample.xcodeproj/project.pbxproj +++ b/Samples/ObjC/SignInSample/SignInSample.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -459,6 +459,7 @@ .., ); INFOPLIST_FILE = "$(SRCROOT)/SignInSample-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -477,6 +478,7 @@ .., ); INFOPLIST_FILE = "$(SRCROOT)/SignInSample-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj b/Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj index ce6f4817..f53071cc 100644 --- a/Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj +++ b/Samples/ObjC/SignInSample/SignInSampleForPod.xcodeproj/project.pbxproj @@ -510,7 +510,7 @@ CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/SignInSample-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.google.SignInSample; PRODUCT_NAME = SignInSample; @@ -527,7 +527,7 @@ CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/SignInSample-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.google.SignInSample; PRODUCT_NAME = SignInSample; From 4502143704d4447c8c9380cbf6846722e868700f Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 28 Feb 2024 11:47:51 -0800 Subject: [PATCH 23/26] Check integration test for presubmit instruction (#368) --- .github/workflows/integration_tests.yml | 40 ++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 0a925f61..dd86ee1a 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -9,10 +9,48 @@ on: jobs: + grab-pr-body: + runs-on: ubuntu-latest + outputs: + PR_BODY: ${{ steps.body.outputs.PR_BODY }} + steps: + - id: body + env: + PR_BODY: ${{ github.event.pull_request.body }} + run: | + { + echo "PR_BODY<> "$GITHUB_OUTPUT" + + check-pr-body-for-key: + runs-on: ubuntu-latest + needs: grab-pr-body + outputs: + RUN_INTEGRATION: ${{ steps.check_key.outputs.RUN_INTEGRATION }} + steps: + - id: check_key + env: + PR_BODY: ${{ needs.grab-pr-body.outputs.PR_BODY }} + SKIP_KEY: "SKIP_INTEGRATION_TESTS=YES" + name: Check for key and set bool to skip integration tests + run: | + if [[ "$PR_BODY" == *"$SKIP_KEY"* ]]; then + echo "Skipping integration tests for PR body:" + echo "$PR_BODY" + echo "RUN_INTEGRATION=no" >> "$GITHUB_OUTPUT" + else + echo "Running integration tests for PR body:" + echo "$PR_BODY" + echo "RUN_INTEGRATION=yes" >> "$GITHUB_OUTPUT" + fi + swift-button-functional-test: runs-on: macOS-12 + needs: check-pr-body-for-key # Don't run if triggered by a PR from a fork since our Secrets won't be provided to the runner. - if: false # Disabled per issue 367; add back here check for PRs from forks + if: ${{ needs.check-pr-body-for-key.outputs.RUN_INTEGRATION == 'yes' }} && "!github.event.pull_request.head.repo.fork" defaults: run: working-directory: Samples/Swift/DaysUntilBirthday From fc9b1b72d78c7c91d6fc2e631c203eacf9fa2c54 Mon Sep 17 00:00:00 2001 From: mdmathias Date: Wed, 6 Mar 2024 12:14:01 -0800 Subject: [PATCH 24/26] Test skip integration key (#374) --- .github/workflows/integration_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index dd86ee1a..4ed7c580 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -50,7 +50,7 @@ jobs: runs-on: macOS-12 needs: check-pr-body-for-key # Don't run if triggered by a PR from a fork since our Secrets won't be provided to the runner. - if: ${{ needs.check-pr-body-for-key.outputs.RUN_INTEGRATION == 'yes' }} && "!github.event.pull_request.head.repo.fork" + if: ${{ needs.check-pr-body-for-key.outputs.RUN_INTEGRATION == 'yes' && !github.event.pull_request.head.repo.fork }} defaults: run: working-directory: Samples/Swift/DaysUntilBirthday From 6f2c39b29981d57fce942c29de7ae3071d2a3af6 Mon Sep 17 00:00:00 2001 From: Matt Mathias Date: Wed, 10 Apr 2024 12:35:41 -0700 Subject: [PATCH 25/26] Remove duplicate dep on AppCheck --- Package.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Package.swift b/Package.swift index 15037e10..ae459135 100644 --- a/Package.swift +++ b/Package.swift @@ -49,10 +49,6 @@ let package = Package( name: "AppCheck", url: "https://github.com/google/app-check.git", "10.18.0" ..< "11.0.0"), - .package( - name: "AppCheck", - url: "https://github.com/google/app-check.git", - "10.18.0" ..< "11.0.0"), .package( name: "GTMAppAuth", url: "https://github.com/google/GTMAppAuth.git", From 5a639eb63f6f3e7ff9672c6674cb9632e49b15b4 Mon Sep 17 00:00:00 2001 From: Matt Mathias Date: Wed, 10 Apr 2024 13:28:00 -0700 Subject: [PATCH 26/26] Update Swift support podspec to match local GSI dep version --- GoogleSignInSwiftSupport.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoogleSignInSwiftSupport.podspec b/GoogleSignInSwiftSupport.podspec index f40df45e..d712e09c 100644 --- a/GoogleSignInSwiftSupport.podspec +++ b/GoogleSignInSwiftSupport.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| 'CoreGraphics', 'SwiftUI', ] - s.dependency 'GoogleSignIn', '~> 7.1' + s.dependency 'GoogleSignIn', '~> 7.1.0-fac-beta-1.0.0' s.resource_bundles = { 'GoogleSignInSwiftSupport_Privacy' => 'GoogleSignInSwift/Sources/Resources/PrivacyInfo.xcprivacy' }