From ec898f3caf86fcc7cebbb048093039677e34b4af Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Thu, 13 Jul 2023 17:56:03 -0400 Subject: [PATCH] [App Check] Add `limitedUse` parameter to `GACAppAttestProvider` (#11545) Added a constructor to `GACAppAttestProvider` with a `limitedUse` parameter to enable short-lived tokens. This was hardcoded to `YES` in PR #11544 but the `limited_use` request parameter causes requests to fail when sending to the `v1` App Check API (requires `v1beta`) -- the param is not ignored in `v1`. The existing constructor defaults to `limited_use` being set to `NO`. Added TODOs to remove or refactor the constructor since the public API may change significantly before the feature launches. --- .../API/GACAppAttestAPIService.h | 10 +++++++- .../API/GACAppAttestAPIService.m | 17 +++++++------ .../AppAttestProvider/GACAppAttestProvider.m | 21 +++++++++++++++- .../Public/AppCheck/GACAppAttestProvider.h | 25 +++++++++++++++++++ .../GACAppAttestAPIServiceTests.m | 3 ++- .../GACAppAttestProviderTests.m | 1 + 6 files changed, 66 insertions(+), 11 deletions(-) diff --git a/AppCheck/Sources/AppAttestProvider/API/GACAppAttestAPIService.h b/AppCheck/Sources/AppAttestProvider/API/GACAppAttestAPIService.h index 43c8761f5d2..a7bc3cb8444 100644 --- a/AppCheck/Sources/AppAttestProvider/API/GACAppAttestAPIService.h +++ b/AppCheck/Sources/AppAttestProvider/API/GACAppAttestAPIService.h @@ -52,12 +52,20 @@ NS_ASSUME_NONNULL_BEGIN @interface GACAppAttestAPIService : NSObject /// Default initializer. +/// +/// TODO(andrewheard): Remove or refactor the `limitedUse` parameter from this constructor when the +/// short-lived (limited-use) token feature is fully implemented. +/// /// @param APIService An instance implementing `GACAppCheckAPIServiceProtocol` to be used to send /// network requests to the App Check backend. /// @param resourceName The name of the resource protected by App Check; for a Firebase App this is /// "projects/{project_id}/apps/{app_id}". +/// @param limitedUse If YES, forces a short-lived token with a 5 minute TTL. - (instancetype)initWithAPIService:(id)APIService - resourceName:(NSString *)resourceName; + resourceName:(NSString *)resourceName + limitedUse:(BOOL)limitedUse NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; @end diff --git a/AppCheck/Sources/AppAttestProvider/API/GACAppAttestAPIService.m b/AppCheck/Sources/AppAttestProvider/API/GACAppAttestAPIService.m index bcd8eeea5a6..479d2a30111 100644 --- a/AppCheck/Sources/AppAttestProvider/API/GACAppAttestAPIService.m +++ b/AppCheck/Sources/AppAttestProvider/API/GACAppAttestAPIService.m @@ -45,27 +45,28 @@ static NSString *const kJSONContentType = @"application/json"; static NSString *const kHTTPMethodPost = @"POST"; -// TODO(andrewheard): Remove constant when limited-use token feature is implemented. -// Value for `kRequestFieldLimitedUse` parameter. When `limited_use` is `YES`, forces a short-lived -// token with a 5 minute TTL. -static const BOOL kLimitedUseValue = YES; - @interface GACAppAttestAPIService () @property(nonatomic, readonly) id APIService; @property(nonatomic, readonly) NSString *resourceName; +// TODO(andrewheard): Remove or refactor property when short-lived token feature is implemented. +// When `YES`, forces a short-lived token with a 5 minute TTL. +@property(nonatomic, readonly) BOOL limitedUse; + @end @implementation GACAppAttestAPIService - (instancetype)initWithAPIService:(id)APIService - resourceName:(NSString *)resourceName { + resourceName:(NSString *)resourceName + limitedUse:(BOOL)limitedUse { self = [super init]; if (self) { _APIService = APIService; _resourceName = [resourceName copy]; + _limitedUse = limitedUse; } return self; } @@ -194,7 +195,7 @@ - (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(N kRequestFieldArtifact : [self base64StringWithData:artifact], kRequestFieldChallenge : [self base64StringWithData:challenge], kRequestFieldAssertion : [self base64StringWithData:assertion], - kRequestFieldLimitedUse : @(kLimitedUseValue) + kRequestFieldLimitedUse : @(self.limitedUse) }; return [self HTTPBodyWithJSONObject:JSONObject]; @@ -217,7 +218,7 @@ - (nullable NSData *)randomChallengeFromResponseBody:(NSData *)response error:(N kRequestFieldKeyID : keyID, kRequestFieldAttestation : [self base64StringWithData:attestation], kRequestFieldChallenge : [self base64StringWithData:challenge], - kRequestFieldLimitedUse : @(kLimitedUseValue) + kRequestFieldLimitedUse : @(self.limitedUse) }; return [self HTTPBodyWithJSONObject:JSONObject]; diff --git a/AppCheck/Sources/AppAttestProvider/GACAppAttestProvider.m b/AppCheck/Sources/AppAttestProvider/GACAppAttestProvider.m index 378d04c6819..413901b8744 100644 --- a/AppCheck/Sources/AppAttestProvider/GACAppAttestProvider.m +++ b/AppCheck/Sources/AppAttestProvider/GACAppAttestProvider.m @@ -146,6 +146,23 @@ - (nullable instancetype)initWithStorageID:(NSString *)storageID keychainAccessGroup:(nullable NSString *)accessGroup requestHooks: (nullable NSArray *)requestHooks { + return [self initWithStorageID:storageID + resourceName:resourceName + baseURL:baseURL + APIKey:APIKey + keychainAccessGroup:accessGroup + limitedUse:NO + requestHooks:requestHooks]; +} + +- (nullable instancetype)initWithStorageID:(NSString *)storageID + resourceName:(NSString *)resourceName + baseURL:(nullable NSString *)baseURL + APIKey:(nullable NSString *)APIKey + keychainAccessGroup:(nullable NSString *)accessGroup + limitedUse:(BOOL)limitedUse + requestHooks: + (nullable NSArray *)requestHooks { #if GAC_APP_ATTEST_SUPPORTED_TARGETS NSURLSession *URLSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; @@ -163,7 +180,9 @@ - (nullable instancetype)initWithStorageID:(NSString *)storageID requestHooks:requestHooks]; GACAppAttestAPIService *appAttestAPIService = - [[GACAppAttestAPIService alloc] initWithAPIService:APIService resourceName:resourceName]; + [[GACAppAttestAPIService alloc] initWithAPIService:APIService + resourceName:resourceName + limitedUse:limitedUse]; GACAppAttestArtifactStorage *artifactStorage = [[GACAppAttestArtifactStorage alloc] initWithKeySuffix:storageKeySuffix diff --git a/AppCheck/Sources/Public/AppCheck/GACAppAttestProvider.h b/AppCheck/Sources/Public/AppCheck/GACAppAttestProvider.h index 6006035dfe4..ad7a7708aa5 100644 --- a/AppCheck/Sources/Public/AppCheck/GACAppAttestProvider.h +++ b/AppCheck/Sources/Public/AppCheck/GACAppAttestProvider.h @@ -51,6 +51,31 @@ NS_SWIFT_NAME(InternalAppAttestProvider) requestHooks: (nullable NSArray *)requestHooks; +/// Initializer with support for short-lived tokens. +/// +/// TODO(andrewheard): Remove or refactor this constructor when the short-lived (limited-use) token +/// feature is fully implemented. +/// +/// @param storageID A unique identifier to differentiate storage keys corresponding to the same +/// `resourceName`; may be a Firebase App Name or an SDK name. +/// @param resourceName The name of the resource protected by App Check; for a Firebase App this is +/// "projects/{project_id}/apps/{app_id}". +/// @param baseURL The base URL for the App Check service; defaults to +/// `https://firebaseappcheck.googleapis.com/v1` if nil. +/// @param APIKey The Google Cloud Platform API key, if needed, or nil. +/// @param accessGroup The Keychain Access Group. +/// @param limitedUse If YES, forces a short-lived token with a 5 minute TTL. +/// @param requestHooks Hooks that will be invoked on requests through this service. +/// @return An instance of `AppAttestProvider` if App Attest is supported or `nil`. +- (nullable instancetype)initWithStorageID:(NSString *)storageID + resourceName:(NSString *)resourceName + baseURL:(nullable NSString *)baseURL + APIKey:(nullable NSString *)APIKey + keychainAccessGroup:(nullable NSString *)accessGroup + limitedUse:(BOOL)limitedUse + requestHooks: + (nullable NSArray *)requestHooks; + @end NS_ASSUME_NONNULL_END diff --git a/AppCheck/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m b/AppCheck/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m index 8d21c95c483..d8d2ac316af 100644 --- a/AppCheck/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m +++ b/AppCheck/Tests/Unit/AppAttestProvider/GACAppAttestAPIServiceTests.m @@ -53,7 +53,8 @@ - (void)setUp { OCMStub([self.mockAPIService baseURL]).andReturn(kBaseURL); self.appAttestAPIService = [[GACAppAttestAPIService alloc] initWithAPIService:self.mockAPIService - resourceName:kResourceName]; + resourceName:kResourceName + limitedUse:NO]; } - (void)tearDown { diff --git a/AppCheck/Tests/Unit/AppAttestProvider/GACAppAttestProviderTests.m b/AppCheck/Tests/Unit/AppAttestProvider/GACAppAttestProviderTests.m index 36b96f84b3b..48e3b7d2572 100644 --- a/AppCheck/Tests/Unit/AppAttestProvider/GACAppAttestProviderTests.m +++ b/AppCheck/Tests/Unit/AppAttestProvider/GACAppAttestProviderTests.m @@ -117,6 +117,7 @@ - (void)testInitWithValidApp { baseURL:nil APIKey:app.options.APIKey keychainAccessGroup:nil + limitedUse:NO requestHooks:nil]); } #endif // !TARGET_OS_MACCATALYST