Skip to content

Commit 77fa63d

Browse files
authored
[App Check] Add DeviceCheck isSupported check (#11663)
Added a DeviceCheck isSupported check and return a FIRAppCheckErrorCodeUnsupported error when not supported (e.g., when running on the simulator or older Macs). This was done to match the behaviour of the FIRAppAttestProvider.
1 parent 4b9cbf6 commit 77fa63d

File tree

3 files changed

+92
-17
lines changed

3 files changed

+92
-17
lines changed

FirebaseAppCheck/Sources/DeviceCheckProvider/FIRDeviceCheckProvider.m

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#import "FirebaseAppCheck/Sources/Core/APIService/FIRAppCheckAPIService.h"
3232
#import "FirebaseAppCheck/Sources/Core/Backoff/FIRAppCheckBackoffWrapper.h"
33+
#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
3334
#import "FirebaseAppCheck/Sources/Core/FIRAppCheckLogger.h"
3435
#import "FirebaseAppCheck/Sources/Core/FIRAppCheckValidator.h"
3536
#import "FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h"
@@ -133,10 +134,26 @@ - (void)getTokenWithCompletion:(void (^)(FIRAppCheckToken *_Nullable token,
133134
#pragma mark - DeviceCheck
134135

135136
- (FBLPromise<NSData *> *)deviceToken {
136-
return [FBLPromise
137-
wrapObjectOrErrorCompletion:^(FBLPromiseObjectOrErrorCompletion _Nonnull handler) {
138-
[self.deviceTokenGenerator generateTokenWithCompletionHandler:handler];
139-
}];
137+
return [self isDeviceCheckSupported].then(^FBLPromise<NSData *> *(NSNull *ignored) {
138+
return [FBLPromise
139+
wrapObjectOrErrorCompletion:^(FBLPromiseObjectOrErrorCompletion _Nonnull handler) {
140+
[self.deviceTokenGenerator generateTokenWithCompletionHandler:handler];
141+
}];
142+
});
143+
}
144+
145+
#pragma mark - Helpers
146+
147+
/// Returns a resolved promise if DeviceCheck is supported and a rejected promise if it is not.
148+
- (FBLPromise<NSNull *> *)isDeviceCheckSupported {
149+
if (self.deviceTokenGenerator.isSupported) {
150+
return [FBLPromise resolvedWith:[NSNull null]];
151+
} else {
152+
NSError *error = [FIRAppCheckErrorUtil unsupportedAttestationProvider:@"DeviceCheckProvider"];
153+
FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
154+
[rejectedPromise reject:error];
155+
return rejectedPromise;
156+
}
140157
}
141158

142159
@end

FirebaseAppCheck/Sources/DeviceCheckProvider/FIRDeviceCheckTokenGenerator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ NS_ASSUME_NONNULL_BEGIN
2020

2121
@protocol FIRDeviceCheckTokenGenerator <NSObject>
2222

23+
@property(getter=isSupported, readonly) BOOL supported;
24+
2325
- (void)generateTokenWithCompletionHandler:(void (^)(NSData* _Nullable token,
2426
NSError* _Nullable error))completion;
2527

FirebaseAppCheck/Tests/Unit/DeviceCheckProvider/FIRDeviceCheckProviderTests.m

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#import <OCMock/OCMock.h>
2020
#import "FBLPromise+Testing.h"
2121

22+
#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
2223
#import "FirebaseAppCheck/Sources/Core/FIRAppCheckToken+Internal.h"
2324
#import "FirebaseAppCheck/Sources/DeviceCheckProvider/API/FIRDeviceCheckAPIService.h"
2425
#import "FirebaseAppCheck/Sources/DeviceCheckProvider/FIRDeviceCheckTokenGenerator.h"
@@ -98,22 +99,25 @@ - (void)testInitWithIncompleteApp {
9899
}
99100

100101
- (void)testGetTokenSuccess {
101-
// 1. Expect device token to be generated.
102+
// 1. Expect FIRDeviceCheckTokenGenerator.isSupported.
103+
OCMExpect([self.fakeTokenGenerator isSupported]).andReturn(YES);
104+
105+
// 2. Expect device token to be generated.
102106
NSData *deviceToken = [NSData data];
103107
id generateTokenArg = [OCMArg invokeBlockWithArgs:deviceToken, [NSNull null], nil];
104108
OCMExpect([self.fakeTokenGenerator generateTokenWithCompletionHandler:generateTokenArg]);
105109

106-
// 2. Expect FAA token to be requested.
110+
// 3. Expect FAA token to be requested.
107111
FIRAppCheckToken *validToken = [[FIRAppCheckToken alloc] initWithToken:@"valid_token"
108112
expirationDate:[NSDate distantFuture]
109113
receivedAtDate:[NSDate date]];
110114
OCMExpect([self.fakeAPIService appCheckTokenWithDeviceToken:deviceToken])
111115
.andReturn([FBLPromise resolvedWith:validToken]);
112116

113-
// 3. Expect backoff wrapper to be used.
117+
// 4. Expect backoff wrapper to be used.
114118
self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"];
115119

116-
// 4. Call getToken and validate the result.
120+
// 5. Call getToken and validate the result.
117121
XCTestExpectation *completionExpectation =
118122
[self expectationWithDescription:@"completionExpectation"];
119123
[self.provider
@@ -129,7 +133,7 @@ - (void)testGetTokenSuccess {
129133
timeout:0.5
130134
enforceOrder:YES];
131135

132-
// 5. Verify.
136+
// 6. Verify.
133137
XCTAssertNil(self.fakeBackoffWrapper.operationError);
134138
FIRAppCheckToken *wrapperResult =
135139
[self.fakeBackoffWrapper.operationResult isKindOfClass:[FIRAppCheckToken class]]
@@ -141,6 +145,52 @@ - (void)testGetTokenSuccess {
141145
OCMVerifyAll(self.fakeTokenGenerator);
142146
}
143147

148+
- (void)testGetTokenWhenDeviceCheckIsNotSupported {
149+
NSError *expectedError =
150+
[FIRAppCheckErrorUtil unsupportedAttestationProvider:@"DeviceCheckProvider"];
151+
152+
// 0.1. Expect backoff wrapper to be used.
153+
self.fakeBackoffWrapper.backoffExpectation = [self expectationWithDescription:@"Backoff"];
154+
155+
// 0.2. Expect default error handler to be used.
156+
XCTestExpectation *errorHandlerExpectation = [self expectationWithDescription:@"Error handler"];
157+
self.fakeBackoffWrapper.defaultErrorHandler = ^FIRAppCheckBackoffType(NSError *_Nonnull error) {
158+
XCTAssertEqualObjects(error, expectedError);
159+
[errorHandlerExpectation fulfill];
160+
return FIRAppCheckBackoffType1Day;
161+
};
162+
163+
// 1. Expect FIRDeviceCheckTokenGenerator.isSupported.
164+
OCMExpect([self.fakeTokenGenerator isSupported]).andReturn(NO);
165+
166+
// 2. Don't expect DeviceCheck token to be generated or FAA token to be requested.
167+
OCMReject([self.fakeTokenGenerator generateTokenWithCompletionHandler:OCMOCK_ANY]);
168+
OCMReject([self.fakeAPIService appCheckTokenWithDeviceToken:OCMOCK_ANY]);
169+
170+
// 3. Call getToken and validate the result.
171+
XCTestExpectation *completionExpectation =
172+
[self expectationWithDescription:@"completionExpectation"];
173+
[self.provider
174+
getTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) {
175+
[completionExpectation fulfill];
176+
XCTAssertNil(token);
177+
XCTAssertEqualObjects(error, expectedError);
178+
}];
179+
180+
[self waitForExpectations:@[
181+
self.fakeBackoffWrapper.backoffExpectation, errorHandlerExpectation, completionExpectation
182+
]
183+
timeout:0.5
184+
enforceOrder:YES];
185+
186+
// 4. Verify.
187+
OCMVerifyAll(self.fakeAPIService);
188+
OCMVerifyAll(self.fakeTokenGenerator);
189+
190+
XCTAssertEqualObjects(self.fakeBackoffWrapper.operationError, expectedError);
191+
XCTAssertNil(self.fakeBackoffWrapper.operationResult);
192+
}
193+
144194
- (void)testGetTokenWhenDeviceTokenFails {
145195
NSError *deviceTokenError = [NSError errorWithDomain:@"FIRDeviceCheckProviderTests"
146196
code:-1
@@ -157,14 +207,17 @@ - (void)testGetTokenWhenDeviceTokenFails {
157207
return FIRAppCheckBackoffType1Day;
158208
};
159209

160-
// 1. Expect device token to be generated.
210+
// 1. Expect FIRDeviceCheckTokenGenerator.isSupported.
211+
OCMExpect([self.fakeTokenGenerator isSupported]).andReturn(YES);
212+
213+
// 2. Expect device token to be generated.
161214
id generateTokenArg = [OCMArg invokeBlockWithArgs:[NSNull null], deviceTokenError, nil];
162215
OCMExpect([self.fakeTokenGenerator generateTokenWithCompletionHandler:generateTokenArg]);
163216

164-
// 2. Don't expect FAA token to be requested.
217+
// 3. Don't expect FAA token to be requested.
165218
OCMReject([self.fakeAPIService appCheckTokenWithDeviceToken:[OCMArg any]]);
166219

167-
// 3. Call getToken and validate the result.
220+
// 4. Call getToken and validate the result.
168221
XCTestExpectation *completionExpectation =
169222
[self expectationWithDescription:@"completionExpectation"];
170223
[self.provider
@@ -180,7 +233,7 @@ - (void)testGetTokenWhenDeviceTokenFails {
180233
timeout:0.5
181234
enforceOrder:YES];
182235

183-
// 4. Verify.
236+
// 5. Verify.
184237
OCMVerifyAll(self.fakeAPIService);
185238
OCMVerifyAll(self.fakeTokenGenerator);
186239

@@ -204,18 +257,21 @@ - (void)testGetTokenWhenAPIServiceFails {
204257
return FIRAppCheckBackoffType1Day;
205258
};
206259

207-
// 1. Expect device token to be generated.
260+
// 1. Expect FIRDeviceCheckTokenGenerator.isSupported.
261+
OCMExpect([self.fakeTokenGenerator isSupported]).andReturn(YES);
262+
263+
// 2. Expect device token to be generated.
208264
NSData *deviceToken = [NSData data];
209265
id generateTokenArg = [OCMArg invokeBlockWithArgs:deviceToken, [NSNull null], nil];
210266
OCMExpect([self.fakeTokenGenerator generateTokenWithCompletionHandler:generateTokenArg]);
211267

212-
// 2. Expect FAA token to be requested.
268+
// 3. Expect FAA token to be requested.
213269
FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
214270
[rejectedPromise reject:APIServiceError];
215271
OCMExpect([self.fakeAPIService appCheckTokenWithDeviceToken:deviceToken])
216272
.andReturn(rejectedPromise);
217273

218-
// 3. Call getToken and validate the result.
274+
// 4. Call getToken and validate the result.
219275
XCTestExpectation *completionExpectation =
220276
[self expectationWithDescription:@"completionExpectation"];
221277
[self.provider
@@ -231,7 +287,7 @@ - (void)testGetTokenWhenAPIServiceFails {
231287
timeout:0.5
232288
enforceOrder:YES];
233289

234-
// 4. Verify.
290+
// 5. Verify.
235291
OCMVerifyAll(self.fakeAPIService);
236292
OCMVerifyAll(self.fakeTokenGenerator);
237293

0 commit comments

Comments
 (0)