From db69e85c4199cc08d46f924b939a19074dae33c8 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 1 Sep 2026 13:16:17 -0700 Subject: [PATCH 1/5] fix: capture request date before network request to prevent clock skew/suspension bugs --- .../AppAttestProvider/API/GACAppAttestAPIService.m | 2 +- .../Sources/Core/APIService/GACAppCheckAPIService.m | 2 +- .../Sources/Core/APIService/GACURLSessionDataResponse.m | 5 ++++- .../Sources/Core/APIService/NSURLSession+GACPromises.m | 4 +++- .../Public/AppCheckCore/_GACURLSessionDataResponse.h | 5 ++++- .../Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m | 2 +- AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m | 8 ++++---- .../GACAppCheckDebugProviderAPIServiceTests.m | 4 ++-- .../DeviceCheckProvider/GACDeviceCheckAPIServiceTests.m | 4 ++-- 9 files changed, 22 insertions(+), 14 deletions(-) diff --git a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m b/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m index 4743a82..7ba2b17 100644 --- a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m +++ b/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m @@ -172,7 +172,7 @@ - (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(N __auto_type response = [[GACAppAttestAttestationResponse alloc] initWithResponseData:URLResponse.HTTPBody - requestDate:[NSDate date] + requestDate:URLResponse.requestDate error:&error]; return response ?: error; diff --git a/AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.m b/AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.m index 0eab6c9..bd43913 100644 --- a/AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.m +++ b/AppCheckCore/Sources/Core/APIService/GACAppCheckAPIService.m @@ -196,7 +196,7 @@ - (instancetype)initWithURLSession:(NSURLSession *)session GACAppCheckToken *token = [[GACAppCheckToken alloc] initWithTokenExchangeResponse:response.HTTPBody - requestDate:[NSDate date] + requestDate:response.requestDate error:&error]; return token ?: error; }]; diff --git a/AppCheckCore/Sources/Core/APIService/GACURLSessionDataResponse.m b/AppCheckCore/Sources/Core/APIService/GACURLSessionDataResponse.m index 1842d1a..8223243 100644 --- a/AppCheckCore/Sources/Core/APIService/GACURLSessionDataResponse.m +++ b/AppCheckCore/Sources/Core/APIService/GACURLSessionDataResponse.m @@ -18,11 +18,14 @@ @implementation _GACURLSessionDataResponse -- (instancetype)initWithResponse:(NSHTTPURLResponse *)response HTTPBody:(NSData *)body { +- (instancetype)initWithResponse:(NSHTTPURLResponse *)response + HTTPBody:(NSData *)body + requestDate:(NSDate *)requestDate { self = [super init]; if (self) { _HTTPResponse = response; _HTTPBody = body; + _requestDate = requestDate; } return self; } diff --git a/AppCheckCore/Sources/Core/APIService/NSURLSession+GACPromises.m b/AppCheckCore/Sources/Core/APIService/NSURLSession+GACPromises.m index f9a79b0..c960d1f 100644 --- a/AppCheckCore/Sources/Core/APIService/NSURLSession+GACPromises.m +++ b/AppCheckCore/Sources/Core/APIService/NSURLSession+GACPromises.m @@ -28,6 +28,7 @@ @implementation NSURLSession (GACPromises) - (FBLPromise<_GACURLSessionDataResponse *> *)gac_dataTaskPromiseWithRequest: (NSURLRequest *)URLRequest { + NSDate *requestDate = [NSDate date]; return [FBLPromise async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject) { [[self dataTaskWithRequest:URLRequest completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, @@ -37,7 +38,8 @@ @implementation NSURLSession (GACPromises) } else { fulfill([[_GACURLSessionDataResponse alloc] initWithResponse:(NSHTTPURLResponse *)response - HTTPBody:data]); + HTTPBody:data + requestDate:requestDate]); } }] resume]; }]; diff --git a/AppCheckCore/Sources/Public/AppCheckCore/_GACURLSessionDataResponse.h b/AppCheckCore/Sources/Public/AppCheckCore/_GACURLSessionDataResponse.h index 07aeb33..15329f3 100644 --- a/AppCheckCore/Sources/Public/AppCheckCore/_GACURLSessionDataResponse.h +++ b/AppCheckCore/Sources/Public/AppCheckCore/_GACURLSessionDataResponse.h @@ -26,8 +26,11 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, readonly) NSHTTPURLResponse *HTTPResponse; @property(nonatomic, nullable, readonly) NSData *HTTPBody; +@property(nonatomic, readonly) NSDate *requestDate; -- (instancetype)initWithResponse:(NSHTTPURLResponse *)response HTTPBody:(nullable NSData *)body; +- (instancetype)initWithResponse:(NSHTTPURLResponse *)response + HTTPBody:(nullable NSData *)body + requestDate:(NSDate *)requestDate; @end diff --git a/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m b/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m index d3157d0..77703a8 100644 --- a/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m +++ b/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m @@ -534,7 +534,7 @@ - (_GACURLSessionDataResponse *)APIResponseWithCode:(NSInteger)code XCTAssertNotNil(responseBody); NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:code]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; return APIResponse; } diff --git a/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m b/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m index 4d2db9f..f27c7f5 100644 --- a/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m +++ b/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m @@ -368,7 +368,7 @@ - (void)testAppCheckTokenWithAPIResponseValidResponse { XCTAssertNotNil(responseBody); NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; // 2. Expected result. NSString *expectedFACToken = @"valid_app_check_token"; @@ -394,7 +394,7 @@ - (void)testAppCheckTokenWithAPIResponseInvalidFormat { NSData *responseBody = [responseBodyString dataUsingEncoding:NSUTF8StringEncoding]; NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; // 2. Parse API response. __auto_type tokenPromise = [self.APIService appCheckTokenWithAPIResponse:APIResponse]; @@ -429,7 +429,7 @@ - (void)assertMissingFieldErrorWithFixture:(NSString *)fixtureName NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:missingFiledBody]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:missingFiledBody requestDate:[NSDate date]]; // 2. Parse API response. __auto_type tokenPromise = [self.APIService appCheckTokenWithAPIResponse:APIResponse]; @@ -466,7 +466,7 @@ - (void)stubURLSessionDataTaskPromiseWithResponse:(NSHTTPURLResponse *)HTTPRespo FBLPromise<_GACURLSessionDataResponse *> *result = [FBLPromise pendingPromise]; if (error == nil) { _GACURLSessionDataResponse *response = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:body]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:body requestDate:[NSDate date]]; [result fulfill:response]; } else { [result reject:error]; diff --git a/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m b/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m index 551b6e7..76a4891 100644 --- a/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m +++ b/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m @@ -77,7 +77,7 @@ - (void)testAppCheckTokenSuccessWithLimitedUse:(BOOL)limitedUse { NSData *fakeResponseData = [@"fake response" dataUsingEncoding:NSUTF8StringEncoding]; NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:fakeResponseData]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:fakeResponseData requestDate:[NSDate date]]; self.mockAPIService.sendRequestPromise = [FBLPromise resolvedWith:APIResponse]; @@ -122,7 +122,7 @@ - (void)testAppCheckTokenResponseParsingError { NSData *fakeResponseData = [@"fake response" dataUsingEncoding:NSUTF8StringEncoding]; NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:fakeResponseData]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:fakeResponseData requestDate:[NSDate date]]; self.mockAPIService.sendRequestPromise = [FBLPromise resolvedWith:APIResponse]; diff --git a/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckAPIServiceTests.m b/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckAPIServiceTests.m index 4db23b1..a835342 100644 --- a/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckAPIServiceTests.m +++ b/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckAPIServiceTests.m @@ -84,7 +84,7 @@ - (void)testAppCheckTokenSuccessWithLimitedUse:(BOOL)limitedUse { NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; self.mockAPIService.sendRequestPromise = [FBLPromise resolvedWith:APIResponse]; @@ -135,7 +135,7 @@ - (void)testAppCheckTokenResponseParsingError { NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; self.mockAPIService.sendRequestPromise = [FBLPromise resolvedWith:APIResponse]; From 75dd06431fb5a9b95e1492a9af2a2f79480c242d Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 1 Sep 2026 13:20:38 -0700 Subject: [PATCH 2/5] style --- .../API/GACAppAttestAPIService.m | 18 +++++++++--------- .../GACAppAttestAPIServiceTests.m | 4 +++- .../Unit/Core/GACAppCheckAPIServiceTests.m | 16 ++++++++++++---- .../GACAppCheckDebugProviderAPIServiceTests.m | 8 ++++++-- .../GACDeviceCheckAPIServiceTests.m | 8 ++++++-- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m b/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m index 7ba2b17..10d4352 100644 --- a/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m +++ b/AppCheckCore/Sources/AppAttestProvider/API/GACAppAttestAPIService.m @@ -166,17 +166,17 @@ - (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(N body:HTTPBody additionalHeaders:@{kContentTypeKey : kJSONContentType}]; }) - .thenOn( - [self backgroundQueue], ^id _Nullable(_GACURLSessionDataResponse *_Nullable URLResponse) { - NSError *error; + .thenOn([self backgroundQueue], + ^id _Nullable(_GACURLSessionDataResponse *_Nullable URLResponse) { + NSError *error; - __auto_type response = - [[GACAppAttestAttestationResponse alloc] initWithResponseData:URLResponse.HTTPBody - requestDate:URLResponse.requestDate - error:&error]; + __auto_type response = [[GACAppAttestAttestationResponse alloc] + initWithResponseData:URLResponse.HTTPBody + requestDate:URLResponse.requestDate + error:&error]; - return response ?: error; - }); + return response ?: error; + }); } #pragma mark - Request HTTP Body diff --git a/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m b/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m index 77703a8..4773c72 100644 --- a/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m +++ b/AppCheckCore/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m @@ -534,7 +534,9 @@ - (_GACURLSessionDataResponse *)APIResponseWithCode:(NSInteger)code XCTAssertNotNil(responseBody); NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:code]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:responseBody + requestDate:[NSDate date]]; return APIResponse; } diff --git a/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m b/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m index f27c7f5..8bb4f3d 100644 --- a/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m +++ b/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m @@ -368,7 +368,9 @@ - (void)testAppCheckTokenWithAPIResponseValidResponse { XCTAssertNotNil(responseBody); NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:responseBody + requestDate:[NSDate date]]; // 2. Expected result. NSString *expectedFACToken = @"valid_app_check_token"; @@ -394,7 +396,9 @@ - (void)testAppCheckTokenWithAPIResponseInvalidFormat { NSData *responseBody = [responseBodyString dataUsingEncoding:NSUTF8StringEncoding]; NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:responseBody + requestDate:[NSDate date]]; // 2. Parse API response. __auto_type tokenPromise = [self.APIService appCheckTokenWithAPIResponse:APIResponse]; @@ -429,7 +433,9 @@ - (void)assertMissingFieldErrorWithFixture:(NSString *)fixtureName NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:missingFiledBody requestDate:[NSDate date]]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:missingFiledBody + requestDate:[NSDate date]]; // 2. Parse API response. __auto_type tokenPromise = [self.APIService appCheckTokenWithAPIResponse:APIResponse]; @@ -466,7 +472,9 @@ - (void)stubURLSessionDataTaskPromiseWithResponse:(NSHTTPURLResponse *)HTTPRespo FBLPromise<_GACURLSessionDataResponse *> *result = [FBLPromise pendingPromise]; if (error == nil) { _GACURLSessionDataResponse *response = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:body requestDate:[NSDate date]]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:body + requestDate:[NSDate date]]; [result fulfill:response]; } else { [result reject:error]; diff --git a/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m b/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m index 76a4891..1ab8f28 100644 --- a/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m +++ b/AppCheckCore/Tests/Unit/DebugProvider/GACAppCheckDebugProviderAPIServiceTests.m @@ -77,7 +77,9 @@ - (void)testAppCheckTokenSuccessWithLimitedUse:(BOOL)limitedUse { NSData *fakeResponseData = [@"fake response" dataUsingEncoding:NSUTF8StringEncoding]; NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:fakeResponseData requestDate:[NSDate date]]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:fakeResponseData + requestDate:[NSDate date]]; self.mockAPIService.sendRequestPromise = [FBLPromise resolvedWith:APIResponse]; @@ -122,7 +124,9 @@ - (void)testAppCheckTokenResponseParsingError { NSData *fakeResponseData = [@"fake response" dataUsingEncoding:NSUTF8StringEncoding]; NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:fakeResponseData requestDate:[NSDate date]]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:fakeResponseData + requestDate:[NSDate date]]; self.mockAPIService.sendRequestPromise = [FBLPromise resolvedWith:APIResponse]; diff --git a/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckAPIServiceTests.m b/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckAPIServiceTests.m index a835342..ceb484c 100644 --- a/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckAPIServiceTests.m +++ b/AppCheckCore/Tests/Unit/DeviceCheckProvider/GACDeviceCheckAPIServiceTests.m @@ -84,7 +84,9 @@ - (void)testAppCheckTokenSuccessWithLimitedUse:(BOOL)limitedUse { NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:responseBody + requestDate:[NSDate date]]; self.mockAPIService.sendRequestPromise = [FBLPromise resolvedWith:APIResponse]; @@ -135,7 +137,9 @@ - (void)testAppCheckTokenResponseParsingError { NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; _GACURLSessionDataResponse *APIResponse = - [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse HTTPBody:responseBody requestDate:[NSDate date]]; + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:responseBody + requestDate:[NSDate date]]; self.mockAPIService.sendRequestPromise = [FBLPromise resolvedWith:APIResponse]; From 9f2a2b99288ca60818ff5cb9ad0c016b47fdde15 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 1 Sep 2026 13:22:48 -0700 Subject: [PATCH 3/5] chore: update CHANGELOG for token expiry fix --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29c0f5e..00abafe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# Unreleased +- [fixed] Fixed an issue where the time-to-live (TTL) for a cached token was calculated from the + moment the token response was processed locally, rather than when the request was initiated, + which could lead to artificially extended token lifetimes during app suspension. + (https://github.com/firebase/firebase-ios-sdk/issues/16573) + # 11.3.1 - [fixed] Added recovery logic to reset and retry attestation when App Attest returns `DCErrorUnknownSystemFailure` during assertion From b2598a9af3d7d2ea7a4eae0c649b6bf79a9c9bf2 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 1 Sep 2026 13:31:47 -0700 Subject: [PATCH 4/5] test: fix swift tests and add missing requestDate argument --- .../Unit/Core/GACAppCheckAPIServiceTests.m | 30 +++++++++++++++++++ .../Tests/MockRecaptchaSupport.swift | 3 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m b/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m index 8bb4f3d..7b72c06 100644 --- a/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m +++ b/AppCheckCore/Tests/Unit/Core/GACAppCheckAPIServiceTests.m @@ -390,6 +390,36 @@ - (void)testAppCheckTokenWithAPIResponseValidResponse { precision:10]); } +- (void)testAppCheckTokenWithAPIResponseUsesRequestDate { + // 1. Prepare input parameters. + NSData *responseBody = + [GACFixtureLoader loadFixtureNamed:@"FACTokenExchangeResponseSuccess.json"]; + XCTAssertNotNil(responseBody); + NSHTTPURLResponse *HTTPResponse = [GACURLSessionFake HTTPResponseWithCode:200]; + NSDate *requestDate = [NSDate dateWithTimeIntervalSince1970:100000]; + _GACURLSessionDataResponse *APIResponse = + [[_GACURLSessionDataResponse alloc] initWithResponse:HTTPResponse + HTTPBody:responseBody + requestDate:requestDate]; + + // 2. Expected result. + NSString *expectedFACToken = @"valid_app_check_token"; + + // 3. Parse API response. + __auto_type tokenPromise = [self.APIService appCheckTokenWithAPIResponse:APIResponse]; + + // 4. Verify. + XCTAssert(FBLWaitForPromisesWithTimeout(1)); + + XCTAssertTrue(tokenPromise.isFulfilled); + XCTAssertNil(tokenPromise.error); + + XCTAssertEqualObjects(tokenPromise.value.token, expectedFACToken); + + NSDate *expectedExpiration = [requestDate dateByAddingTimeInterval:1800]; + XCTAssertEqualObjects(tokenPromise.value.expirationDate, expectedExpiration); +} + - (void)testAppCheckTokenWithAPIResponseInvalidFormat { // 1. Prepare input parameters. NSString *responseBodyString = @"Token verification failed."; diff --git a/AppCheckRecaptchaProvider/Tests/MockRecaptchaSupport.swift b/AppCheckRecaptchaProvider/Tests/MockRecaptchaSupport.swift index e62162a..3f622c1 100644 --- a/AppCheckRecaptchaProvider/Tests/MockRecaptchaSupport.swift +++ b/AppCheckRecaptchaProvider/Tests/MockRecaptchaSupport.swift @@ -110,7 +110,8 @@ class MockAppCheckCoreAPIService: NSObject, _GACAppCheckAPIServiceProtocol { } else { let response = expectedResponse ?? _GACURLSessionDataResponse( response: HTTPURLResponse(), - httpBody: Data() + httpBody: Data(), + requestDate: Date() ) promise.fulfill(response) } From a336301616536957a0ab177f73a17e2c435192e7 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 1 Sep 2026 13:54:49 -0700 Subject: [PATCH 5/5] fix: correct Swift bridging label for requestDate initializer --- AppCheckRecaptchaProvider/Tests/MockRecaptchaSupport.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppCheckRecaptchaProvider/Tests/MockRecaptchaSupport.swift b/AppCheckRecaptchaProvider/Tests/MockRecaptchaSupport.swift index 3f622c1..26f972e 100644 --- a/AppCheckRecaptchaProvider/Tests/MockRecaptchaSupport.swift +++ b/AppCheckRecaptchaProvider/Tests/MockRecaptchaSupport.swift @@ -111,7 +111,7 @@ class MockAppCheckCoreAPIService: NSObject, _GACAppCheckAPIServiceProtocol { let response = expectedResponse ?? _GACURLSessionDataResponse( response: HTTPURLResponse(), httpBody: Data(), - requestDate: Date() + request: Date() ) promise.fulfill(response) }