Skip to content

Commit f6b4582

Browse files
ncooke3hiteshmaurya56paulb777
authored
feat: Add Recaptcha attestation provider (#94)
Signed-off-by: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Co-authored-by: Hitesh Maurya <hiteshmaurya@google.com> Co-authored-by: Paul Beusterien <paulbeusterien@google.com>
1 parent f50adb5 commit f6b4582

57 files changed

Lines changed: 1869 additions & 272 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/app_check_core.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ jobs:
3232
run: ./scripts/setup_bundler.sh
3333
- name: Select Xcode version
3434
run: sudo xcode-select -s /Applications/${{ matrix.xcode }}.app/Contents/Developer
35+
- name: Install simulators in case they are missing.
36+
if: contains(matrix.target, 'macos') == false
37+
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3
38+
with:
39+
timeout_minutes: 15
40+
max_attempts: 5
41+
retry_wait_seconds: 120
42+
continue_on_error: true
43+
command: xcodebuild -downloadPlatform ${{ matrix.target }}
3544
- name: Build and test
3645
run: |
3746
scripts/third_party/travis/retry.sh scripts/pod_lib_lint.rb AppCheckCore.podspec \

.github/workflows/spm.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ jobs:
3232
- name: Initialize xcodebuild
3333
run: xcodebuild -list
3434
- name: iOS Unit Tests
35-
run: scripts/third_party/travis/retry.sh scripts/build.sh AppCheck ${{ matrix.platform }} spm
35+
run: scripts/third_party/travis/retry.sh scripts/build.sh AppCheck-Package ${{ matrix.platform }} spm

AppCheckCore.podspec

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'AppCheckCore'
3-
s.version = '11.2.0'
3+
s.version = '11.3.0'
44
s.summary = 'App Check Core SDK.'
55

66
s.description = <<-DESC
@@ -37,15 +37,20 @@ Pod::Spec.new do |s|
3737
s.source_files = [
3838
base_dir + 'Sources/**/*.[mh]',
3939
]
40+
s.ios.source_files = [
41+
'AppCheckRecaptchaProvider/Sources/**/*.swift',
42+
]
4043
s.public_header_files = base_dir + 'Sources/Public/AppCheckCore/*.h'
4144

4245
s.ios.weak_framework = 'DeviceCheck'
4346
s.osx.weak_framework = 'DeviceCheck'
4447
s.tvos.weak_framework = 'DeviceCheck'
4548

4649
s.dependency 'PromisesObjC', '~> 2.4'
50+
s.dependency 'PromisesSwift', '~> 2.4'
4751
s.dependency 'GoogleUtilities/Environment', '~> 8.0'
4852
s.dependency 'GoogleUtilities/UserDefaults', '~> 8.0'
53+
s.ios.dependency 'RecaptchaInterop', '~> 101.0'
4954

5055
s.pod_target_xcconfig = {
5156
'GCC_C_LANGUAGE_STANDARD' => 'c99',

AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@class FBLPromise<Result>;
2020
@class GACAppAttestAttestationResponse;
2121
@class GACAppCheckToken;
22-
@protocol GACAppCheckAPIServiceProtocol;
22+
@protocol _GACAppCheckAPIServiceProtocol;
2323

2424
NS_ASSUME_NONNULL_BEGIN
2525

@@ -55,11 +55,11 @@ NS_ASSUME_NONNULL_BEGIN
5555

5656
/// Default initializer.
5757
///
58-
/// @param APIService An instance implementing `GACAppCheckAPIServiceProtocol` to be used to send
58+
/// @param APIService An instance implementing `_GACAppCheckAPIServiceProtocol` to be used to send
5959
/// network requests to the App Check backend.
6060
/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is
6161
/// "projects/{project_id}/apps/{app_id}".
62-
- (instancetype)initWithAPIService:(id<GACAppCheckAPIServiceProtocol>)APIService
62+
- (instancetype)initWithAPIService:(id<_GACAppCheckAPIServiceProtocol>)APIService
6363
resourceName:(NSString *)resourceName NS_DESIGNATED_INITIALIZER;
6464

6565
- (instancetype)init NS_UNAVAILABLE;

AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
#endif
2424

2525
#import "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h"
26-
#import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h"
27-
#import "AppCheckCore/Sources/Core/APIService/GACURLSessionDataResponse.h"
28-
#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h"
26+
#import "AppCheckCore/Sources/Public/AppCheckCore/_GACAppCheckAPIService.h"
27+
#import "AppCheckCore/Sources/Public/AppCheckCore/_GACAppCheckErrorUtil.h"
28+
#import "AppCheckCore/Sources/Public/AppCheckCore/_GACURLSessionDataResponse.h"
2929

3030
NS_ASSUME_NONNULL_BEGIN
3131

@@ -46,15 +46,15 @@
4646

4747
@interface GACAppAttestAPIService ()
4848

49-
@property(nonatomic, readonly) id<GACAppCheckAPIServiceProtocol> APIService;
49+
@property(nonatomic, readonly) id<_GACAppCheckAPIServiceProtocol> APIService;
5050

5151
@property(nonatomic, readonly) NSString *resourceName;
5252

5353
@end
5454

5555
@implementation GACAppAttestAPIService
5656

57-
- (instancetype)initWithAPIService:(id<GACAppCheckAPIServiceProtocol>)APIService
57+
- (instancetype)initWithAPIService:(id<_GACAppCheckAPIServiceProtocol>)APIService
5858
resourceName:(NSString *)resourceName {
5959
self = [super init];
6060
if (self) {
@@ -76,13 +76,13 @@ - (instancetype)initWithAPIService:(id<GACAppCheckAPIServiceProtocol>)APIService
7676
challenge:challenge
7777
assertion:assertion
7878
limitedUse:limitedUse]
79-
.then(^FBLPromise<GACURLSessionDataResponse *> *(NSData *HTTPBody) {
79+
.then(^FBLPromise<_GACURLSessionDataResponse *> *(NSData *HTTPBody) {
8080
return [self.APIService sendRequestWithURL:URL
8181
HTTPMethod:kHTTPMethodPost
8282
body:HTTPBody
8383
additionalHeaders:@{kContentTypeKey : kJSONContentType}];
8484
})
85-
.then(^id _Nullable(GACURLSessionDataResponse *_Nullable response) {
85+
.then(^id _Nullable(_GACURLSessionDataResponse *_Nullable response) {
8686
return [self.APIService appCheckTokenWithAPIResponse:response];
8787
});
8888
}
@@ -99,14 +99,14 @@ - (instancetype)initWithAPIService:(id<GACAppCheckAPIServiceProtocol>)APIService
9999
body:nil
100100
additionalHeaders:nil];
101101
}]
102-
.then(^id _Nullable(GACURLSessionDataResponse *_Nullable response) {
102+
.then(^id _Nullable(_GACURLSessionDataResponse *_Nullable response) {
103103
return [self randomChallengeWithAPIResponse:response];
104104
});
105105
}
106106

107107
#pragma mark - Challenge response parsing
108108

109-
- (FBLPromise<NSData *> *)randomChallengeWithAPIResponse:(GACURLSessionDataResponse *)response {
109+
- (FBLPromise<NSData *> *)randomChallengeWithAPIResponse:(_GACURLSessionDataResponse *)response {
110110
return [FBLPromise onQueue:[self backgroundQueue]
111111
do:^id _Nullable {
112112
NSError *error;
@@ -122,7 +122,7 @@ - (instancetype)initWithAPIService:(id<GACAppCheckAPIServiceProtocol>)APIService
122122
- (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(NSError **)outError {
123123
if (response.length <= 0) {
124124
GACAppCheckSetErrorToPointer(
125-
[GACAppCheckErrorUtil errorWithFailureReason:@"Empty server response body."], outError);
125+
[_GACAppCheckErrorUtil errorWithFailureReason:@"Empty server response body."], outError);
126126
return nil;
127127
}
128128

@@ -132,14 +132,15 @@ - (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(N
132132
error:&JSONError];
133133

134134
if (![responseDict isKindOfClass:[NSDictionary class]]) {
135-
GACAppCheckSetErrorToPointer([GACAppCheckErrorUtil JSONSerializationError:JSONError], outError);
135+
GACAppCheckSetErrorToPointer([_GACAppCheckErrorUtil JSONSerializationError:JSONError],
136+
outError);
136137
return nil;
137138
}
138139

139140
NSString *challenge = responseDict[@"challenge"];
140141
if (![challenge isKindOfClass:[NSString class]]) {
141142
GACAppCheckSetErrorToPointer(
142-
[GACAppCheckErrorUtil appCheckTokenResponseErrorWithMissingField:@"challenge"], outError);
143+
[_GACAppCheckErrorUtil appCheckTokenResponseErrorWithMissingField:@"challenge"], outError);
143144
return nil;
144145
}
145146

@@ -159,14 +160,14 @@ - (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(N
159160
keyID:keyID
160161
challenge:challenge
161162
limitedUse:limitedUse]
162-
.then(^FBLPromise<GACURLSessionDataResponse *> *(NSData *HTTPBody) {
163+
.then(^FBLPromise<_GACURLSessionDataResponse *> *(NSData *HTTPBody) {
163164
return [self.APIService sendRequestWithURL:URL
164165
HTTPMethod:kHTTPMethodPost
165166
body:HTTPBody
166167
additionalHeaders:@{kContentTypeKey : kJSONContentType}];
167168
})
168169
.thenOn(
169-
[self backgroundQueue], ^id _Nullable(GACURLSessionDataResponse *_Nullable URLResponse) {
170+
[self backgroundQueue], ^id _Nullable(_GACURLSessionDataResponse *_Nullable URLResponse) {
170171
NSError *error;
171172

172173
__auto_type response =
@@ -186,7 +187,7 @@ - (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(N
186187
limitedUse:(BOOL)limitedUse {
187188
if (artifact.length <= 0 || challenge.length <= 0 || assertion.length <= 0) {
188189
FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
189-
[rejectedPromise reject:[GACAppCheckErrorUtil
190+
[rejectedPromise reject:[_GACAppCheckErrorUtil
190191
errorWithFailureReason:@"Missing or empty request parameter."]];
191192
return rejectedPromise;
192193
}
@@ -210,7 +211,7 @@ - (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(N
210211
limitedUse:(BOOL)limitedUse {
211212
if (attestation.length <= 0 || keyID.length <= 0 || challenge.length <= 0) {
212213
FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
213-
[rejectedPromise reject:[GACAppCheckErrorUtil
214+
[rejectedPromise reject:[_GACAppCheckErrorUtil
214215
errorWithFailureReason:@"Missing or empty request parameter."]];
215216
return rejectedPromise;
216217
}
@@ -237,7 +238,7 @@ - (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(N
237238
if (payloadJSON) {
238239
[HTTPBodyPromise fulfill:payloadJSON];
239240
} else {
240-
[HTTPBodyPromise reject:[GACAppCheckErrorUtil JSONSerializationError:encodingError]];
241+
[HTTPBodyPromise reject:[_GACAppCheckErrorUtil JSONSerializationError:encodingError]];
241242
}
242243
return HTTPBodyPromise;
243244
}

AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.m

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#import "AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAttestationResponse.h"
1818

1919
#import "AppCheckCore/Sources/Core/APIService/GACAppCheckToken+APIResponse.h"
20-
#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h"
20+
#import "AppCheckCore/Sources/Public/AppCheckCore/_GACAppCheckErrorUtil.h"
2121

2222
static NSString *const kResponseFieldAppCheckTokenDict = @"appCheckToken";
2323
static NSString *const kResponseFieldArtifact = @"artifact";
@@ -38,7 +38,7 @@ - (nullable instancetype)initWithResponseData:(NSData *)response
3838
error:(NSError **)outError {
3939
if (response.length <= 0) {
4040
GACAppCheckSetErrorToPointer(
41-
[GACAppCheckErrorUtil
41+
[_GACAppCheckErrorUtil
4242
errorWithFailureReason:
4343
@"Failed to parse the initial handshake response. Empty server response body."],
4444
outError);
@@ -51,14 +51,15 @@ - (nullable instancetype)initWithResponseData:(NSData *)response
5151
error:&JSONError];
5252

5353
if (![responseDict isKindOfClass:[NSDictionary class]]) {
54-
GACAppCheckSetErrorToPointer([GACAppCheckErrorUtil JSONSerializationError:JSONError], outError);
54+
GACAppCheckSetErrorToPointer([_GACAppCheckErrorUtil JSONSerializationError:JSONError],
55+
outError);
5556
return nil;
5657
}
5758

5859
NSString *artifactBase64String = responseDict[kResponseFieldArtifact];
5960
if (![artifactBase64String isKindOfClass:[NSString class]]) {
6061
GACAppCheckSetErrorToPointer(
61-
[GACAppCheckErrorUtil
62+
[_GACAppCheckErrorUtil
6263
appAttestAttestationResponseErrorWithMissingField:kResponseFieldArtifact],
6364
outError);
6465
return nil;
@@ -67,7 +68,7 @@ - (nullable instancetype)initWithResponseData:(NSData *)response
6768
options:0];
6869
if (artifactData == nil) {
6970
GACAppCheckSetErrorToPointer(
70-
[GACAppCheckErrorUtil
71+
[_GACAppCheckErrorUtil
7172
appAttestAttestationResponseErrorWithMissingField:kResponseFieldArtifact],
7273
outError);
7374
return nil;
@@ -76,7 +77,7 @@ - (nullable instancetype)initWithResponseData:(NSData *)response
7677
NSDictionary *appCheckTokenDict = responseDict[kResponseFieldAppCheckTokenDict];
7778
if (![appCheckTokenDict isKindOfClass:[NSDictionary class]]) {
7879
GACAppCheckSetErrorToPointer(
79-
[GACAppCheckErrorUtil
80+
[_GACAppCheckErrorUtil
8081
appAttestAttestationResponseErrorWithMissingField:kResponseFieldAppCheckTokenDict],
8182
outError);
8283
return nil;

AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
#import "AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.h"
2020

21-
#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h"
21+
#import "AppCheckCore/Sources/Public/AppCheckCore/_GACAppCheckErrorUtil.h"
2222

2323
@implementation GACAppAttestRejectionError
2424

AppCheckCore/Sources/AppAttestProvider/GACAppAttestProvider.m

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,17 @@
3030
#import "AppCheckCore/Sources/AppAttestProvider/GACAppAttestService.h"
3131
#import "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestArtifactStorage.h"
3232
#import "AppCheckCore/Sources/AppAttestProvider/Storage/GACAppAttestKeyIDStorage.h"
33-
#import "AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.h"
34-
#import "AppCheckCore/Sources/Core/Backoff/GACAppCheckBackoffWrapper.h"
3533
#import "AppCheckCore/Sources/Core/GACAppCheckLogger+Internal.h"
3634
#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h"
35+
#import "AppCheckCore/Sources/Public/AppCheckCore/_GACAppCheckAPIService.h"
36+
#import "AppCheckCore/Sources/Public/AppCheckCore/_GACAppCheckBackoffWrapper.h"
3737

3838
#import "AppCheckCore/Sources/Core/Utils/GACAppCheckCryptoUtils.h"
3939

4040
#import "AppCheckCore/Sources/AppAttestProvider/Errors/GACAppAttestRejectionError.h"
41-
#import "AppCheckCore/Sources/Core/Errors/GACAppCheckErrorUtil.h"
4241
#import "AppCheckCore/Sources/Core/Errors/GACAppCheckHTTPError.h"
4342
#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h"
43+
#import "AppCheckCore/Sources/Public/AppCheckCore/_GACAppCheckErrorUtil.h"
4444

4545
NS_ASSUME_NONNULL_BEGIN
4646

@@ -108,7 +108,7 @@ @interface GACAppAttestProvider ()
108108
@property(nonatomic, readonly) id<GACAppAttestService> appAttestService;
109109
@property(nonatomic, readonly) id<GACAppAttestKeyIDStorageProtocol> keyIDStorage;
110110
@property(nonatomic, readonly) id<GACAppAttestArtifactStorageProtocol> artifactStorage;
111-
@property(nonatomic, readonly) id<GACAppCheckBackoffWrapperProtocol> backoffWrapper;
111+
@property(nonatomic, readonly) id<_GACAppCheckBackoffWrapperProtocol> backoffWrapper;
112112

113113
@property(nonatomic, nullable) FBLPromise<GACAppCheckToken *> *ongoingGetTokenOperation;
114114
@property(nonatomic, assign) BOOL ongoingGetTokenOperationLimitedUse;
@@ -123,7 +123,7 @@ - (instancetype)initWithAppAttestService:(id<GACAppAttestService>)appAttestServi
123123
APIService:(id<GACAppAttestAPIServiceProtocol>)APIService
124124
keyIDStorage:(id<GACAppAttestKeyIDStorageProtocol>)keyIDStorage
125125
artifactStorage:(id<GACAppAttestArtifactStorageProtocol>)artifactStorage
126-
backoffWrapper:(id<GACAppCheckBackoffWrapperProtocol>)backoffWrapper {
126+
backoffWrapper:(id<_GACAppCheckBackoffWrapperProtocol>)backoffWrapper {
127127
self = [super init];
128128
if (self) {
129129
_appAttestService = appAttestService;
@@ -151,11 +151,11 @@ - (instancetype)initWithServiceName:(NSString *)serviceName
151151
GACAppAttestKeyIDStorage *keyIDStorage =
152152
[[GACAppAttestKeyIDStorage alloc] initWithKeySuffix:storageKeySuffix];
153153

154-
GACAppCheckAPIService *APIService =
155-
[[GACAppCheckAPIService alloc] initWithURLSession:URLSession
156-
baseURL:baseURL
157-
APIKey:APIKey
158-
requestHooks:requestHooks];
154+
_GACAppCheckAPIService *APIService =
155+
[[_GACAppCheckAPIService alloc] initWithURLSession:URLSession
156+
baseURL:baseURL
157+
APIKey:APIKey
158+
requestHooks:requestHooks];
159159

160160
GACAppAttestAPIService *appAttestAPIService =
161161
[[GACAppAttestAPIService alloc] initWithAPIService:APIService resourceName:resourceName];
@@ -164,7 +164,7 @@ - (instancetype)initWithServiceName:(NSString *)serviceName
164164
[[GACAppAttestArtifactStorage alloc] initWithKeySuffix:storageKeySuffix
165165
accessGroup:accessGroup];
166166

167-
GACAppCheckBackoffWrapper *backoffWrapper = [[GACAppCheckBackoffWrapper alloc] init];
167+
_GACAppCheckBackoffWrapper *backoffWrapper = [[_GACAppCheckBackoffWrapper alloc] init];
168168

169169
return [self initWithAppAttestService:DCAppAttestService.sharedService
170170
APIService:appAttestAPIService
@@ -341,9 +341,10 @@ - (void)getTokenWithLimitedUse:(BOOL)limitedUse
341341
completionHandler:handler];
342342
}]
343343
.recoverOn(self.queue, ^id(NSError *error) {
344-
return [GACAppCheckErrorUtil appAttestAttestKeyFailedWithError:error
345-
keyId:keyID
346-
clientDataHash:challengeHash];
344+
return
345+
[_GACAppCheckErrorUtil appAttestAttestKeyFailedWithError:error
346+
keyId:keyID
347+
clientDataHash:challengeHash];
347348
});
348349
})
349350
.thenOn(self.queue, ^FBLPromise<GACAppAttestKeyAttestationResult *> *(NSData *attestation) {
@@ -488,7 +489,7 @@ - (void)getTokenWithLimitedUse:(BOOL)limitedUse
488489
completionHandler:handler];
489490
}]
490491
.recoverOn(self.queue, ^id(NSError *appAttestError) {
491-
NSError *error = [GACAppCheckErrorUtil
492+
NSError *error = [_GACAppCheckErrorUtil
492493
appAttestGenerateAssertionFailedWithError:appAttestError
493494
keyId:keyID
494495
clientDataHash:statementHash];
@@ -570,7 +571,7 @@ - (void)getTokenWithLimitedUse:(BOOL)limitedUse
570571
if (self.appAttestService.isSupported) {
571572
return [FBLPromise resolvedWith:[NSNull null]];
572573
} else {
573-
NSError *error = [GACAppCheckErrorUtil unsupportedAttestationProvider:@"AppAttestProvider"];
574+
NSError *error = [_GACAppCheckErrorUtil unsupportedAttestationProvider:@"AppAttestProvider"];
574575
FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
575576
[rejectedPromise reject:error];
576577
return rejectedPromise;
@@ -596,7 +597,7 @@ - (void)getTokenWithLimitedUse:(BOOL)limitedUse
596597
}]
597598
.recoverOn(self.queue,
598599
^id(NSError *error) {
599-
return [GACAppCheckErrorUtil appAttestGenerateKeyFailedWithError:error];
600+
return [_GACAppCheckErrorUtil appAttestGenerateKeyFailedWithError:error];
600601
})
601602
.thenOn(self.queue, ^FBLPromise<NSString *> *(NSString *keyID) {
602603
return [self.keyIDStorage setAppAttestKeyID:keyID];

0 commit comments

Comments
 (0)