Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@ NS_ASSUME_NONNULL_BEGIN
@interface GACAppCheckDebugProviderAPIService
: NSObject <GACAppCheckDebugProviderAPIServiceProtocol>

/// Default initializer.
/// @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}". See https://google.aip.dev/122 for more details about
/// resource names.
- (instancetype)initWithAPIService:(id<GACAppCheckAPIServiceProtocol>)APIService
projectID:(NSString *)projectID
appID:(NSString *)appID;
resourceName:(NSString *)resourceName;

@end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
#import "AppCheck/Sources/Core/Errors/GACAppCheckErrorUtil.h"
#import "AppCheck/Sources/Core/GACAppCheckLogger.h"

#import "FirebaseCore/Extension/FirebaseCoreInternal.h"

NS_ASSUME_NONNULL_BEGIN

static NSString *const kContentTypeKey = @"Content-Type";
Expand All @@ -43,31 +41,27 @@ @interface GACAppCheckDebugProviderAPIService ()

@property(nonatomic, readonly) id<GACAppCheckAPIServiceProtocol> APIService;

@property(nonatomic, readonly) NSString *projectID;
@property(nonatomic, readonly) NSString *appID;
@property(nonatomic, readonly) NSString *resourceName;

@end

@implementation GACAppCheckDebugProviderAPIService

- (instancetype)initWithAPIService:(id<GACAppCheckAPIServiceProtocol>)APIService
projectID:(NSString *)projectID
appID:(NSString *)appID {
resourceName:(NSString *)resourceName {
self = [super init];
if (self) {
_APIService = APIService;
_projectID = projectID;
_appID = appID;
_resourceName = resourceName;
}
return self;
}

#pragma mark - Public API

- (FBLPromise<GACAppCheckToken *> *)appCheckTokenWithDebugToken:(NSString *)debugToken {
NSString *URLString =
[NSString stringWithFormat:@"%@/projects/%@/apps/%@:exchangeDebugToken",
self.APIService.baseURL, self.projectID, self.appID];
NSString *URLString = [NSString
stringWithFormat:@"%@/%@:exchangeDebugToken", self.APIService.baseURL, self.resourceName];
NSURL *URL = [NSURL URLWithString:URLString];

return [self HTTPBodyWithDebugToken:debugToken]
Expand Down
40 changes: 9 additions & 31 deletions AppCheck/Sources/DebugProvider/GACAppCheckDebugProvider.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,16 @@

#import "AppCheck/Sources/Core/APIService/GACAppCheckAPIService.h"
#import "AppCheck/Sources/Core/GACAppCheckLogger.h"
#import "AppCheck/Sources/Core/GACAppCheckValidator.h"
#import "AppCheck/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h"
#import "AppCheck/Sources/Public/AppCheck/GACAppCheckToken.h"

#import "FirebaseCore/Extension/FirebaseCoreInternal.h"

NS_ASSUME_NONNULL_BEGIN

// TODO(andrewheard): Remove from generic App Check SDK.
// TODO(andrewheard): Parameterize the following Firebase-specific keys.
// FIREBASE_APP_CHECK_ONLY_BEGIN
static NSString *const kHeartbeatKey = @"X-firebase-client";
// FIREBASE_APP_CHECK_ONLY_END

static NSString *const kDebugTokenEnvKey = @"FIRAAppCheckDebugToken";
static NSString *const kDebugTokenUserDefaultsKey = @"FIRAAppCheckDebugToken";
// FIREBASE_APP_CHECK_ONLY_END

@interface GACAppCheckDebugProvider ()
@property(nonatomic, readonly) id<GACAppCheckDebugProviderAPIServiceProtocol> APIService;
Expand All @@ -54,38 +49,21 @@ - (instancetype)initWithAPIService:(id<GACAppCheckDebugProviderAPIServiceProtoco
return self;
}

- (nullable instancetype)initWithApp:(FIRApp *)app {
NSArray<NSString *> *missingOptionsFields =
[GACAppCheckValidator tokenExchangeMissingFieldsInOptions:app.options];
if (missingOptionsFields.count > 0) {
GACLogError(kFIRLoggerAppCheckMessageDebugProviderIncompleteFIROptions,
@"Cannot instantiate `GACAppCheckDebugProvider` for app: %@. The following "
@"`FirebaseOptions` fields are missing: %@",
app.name, [missingOptionsFields componentsJoinedByString:@", "]);
return nil;
}

- (instancetype)initWithStorageID:(NSString *)storageID
resourceName:(NSString *)resourceName
APIKey:(nullable NSString *)APIKey
requestHooks:(nullable NSArray<GACAppCheckAPIRequestHook> *)requestHooks {
NSURLSession *URLSession = [NSURLSession
sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];

// TODO(andrewheard): Remove from generic App Check SDK.
// FIREBASE_APP_CHECK_ONLY_BEGIN
GACAppCheckAPIRequestHook heartbeatLoggerHook = ^(NSMutableURLRequest *request) {
[request setValue:FIRHeaderValueFromHeartbeatsPayload(
[app.heartbeatLogger flushHeartbeatsIntoPayload])
forHTTPHeaderField:kHeartbeatKey];
};
// FIREBASE_APP_CHECK_ONLY_END

GACAppCheckAPIService *APIService =
[[GACAppCheckAPIService alloc] initWithURLSession:URLSession
APIKey:app.options.APIKey
requestHooks:@[ heartbeatLoggerHook ]];
APIKey:APIKey
requestHooks:requestHooks];

GACAppCheckDebugProviderAPIService *debugAPIService =
[[GACAppCheckDebugProviderAPIService alloc] initWithAPIService:APIService
projectID:app.options.projectID
appID:app.options.googleAppID];
resourceName:resourceName];

return [self initWithAPIService:debugAPIService];
}
Expand Down
14 changes: 12 additions & 2 deletions AppCheck/Sources/Public/AppCheck/GACAppCheckDebugProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

#import "GACAppCheckProvider.h"

@class FIRApp;
@protocol GACAppCheckDebugProviderAPIServiceProtocol;

NS_ASSUME_NONNULL_BEGIN
Expand Down Expand Up @@ -66,7 +65,18 @@ NS_SWIFT_NAME(InternalAppCheckDebugProvider)

- (instancetype)init NS_UNAVAILABLE;

- (nullable instancetype)initWithApp:(FIRApp *)app;
/// The default initializer.
/// @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 APIKey The Google Cloud Platform API key, if needed, or nil.
/// @param requestHooks Hooks that will be invoked on requests through this service.
/// @return An instance of `AppCheckDebugProvider` .
- (instancetype)initWithStorageID:(NSString *)storageID
resourceName:(NSString *)resourceName
APIKey:(nullable NSString *)APIKey
requestHooks:(nullable NSArray<GACAppCheckAPIRequestHook> *)requestHooks;

/** Return the locally generated token. */
- (NSString *)localDebugToken;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,30 @@

#import "SharedTestUtilities/URLSession/FIRURLSessionOCMockStub.h"

static NSString *const kProjectID = @"project_id";
static NSString *const kAppID = @"app_id";

@interface GACAppCheckDebugProviderAPIServiceTests : XCTestCase
@property(nonatomic) GACAppCheckDebugProviderAPIService *debugAPIService;

@property(nonatomic) id mockAPIService;

@property(nonatomic) NSString *projectID;
@property(nonatomic) NSString *appID;
@property(nonatomic) NSString *resourceName;
@end

@implementation GACAppCheckDebugProviderAPIServiceTests

- (void)setUp {
[super setUp];

self.projectID = @"project_id";
self.appID = @"app_id";
self.resourceName = [NSString stringWithFormat:@"projects/%@/apps/%@", kProjectID, kAppID];

self.mockAPIService = OCMProtocolMock(@protocol(GACAppCheckAPIServiceProtocol));
OCMStub([self.mockAPIService baseURL]).andReturn(@"https://test.appcheck.url.com/alpha");

self.debugAPIService =
[[GACAppCheckDebugProviderAPIService alloc] initWithAPIService:self.mockAPIService
projectID:self.projectID
appID:self.appID];
resourceName:self.resourceName];
}

- (void)tearDown {
Expand Down
31 changes: 15 additions & 16 deletions AppCheck/Tests/Unit/DebugProvider/GACAppCheckDebugProviderTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,11 @@ - (void)testInitWithValidApp {
options.projectID = @"project_id";
FIRApp *app = [[FIRApp alloc] initInstanceWithName:@"testInitWithValidApp" options:options];

XCTAssertNotNil([[GACAppCheckDebugProvider alloc] initWithApp:app]);
}

- (void)testInitWithIncompleteApp {
FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"app_id" GCMSenderID:@"sender_id"];

options.projectID = @"project_id";
FIRApp *missingAPIKeyApp = [[FIRApp alloc] initInstanceWithName:@"testInitWithValidApp"
options:options];
XCTAssertNil([[GACAppCheckDebugProvider alloc] initWithApp:missingAPIKeyApp]);

options.projectID = nil;
options.APIKey = @"api_key";
FIRApp *missingProjectIDApp = [[FIRApp alloc] initInstanceWithName:@"testInitWithValidApp"
options:options];
XCTAssertNil([[GACAppCheckDebugProvider alloc] initWithApp:missingProjectIDApp]);
XCTAssertNotNil([[GACAppCheckDebugProvider alloc]
initWithStorageID:options.googleAppID
resourceName:[GACAppCheckDebugProviderTests resourceNameFromApp:app]
APIKey:app.options.APIKey
requestHooks:nil]);
}

#pragma mark - Debug token generating/storing
Expand Down Expand Up @@ -179,4 +168,14 @@ - (void)validateGetToken:(GACAppCheckTokenValidationBlock)validationBlock {
[self waitForExpectations:@[ expectation ] timeout:0.5];
}

// TODO(andrewheard): Remove from generic App Check SDK.
// FIREBASE_APP_CHECK_ONLY_BEGIN

+ (NSString *)resourceNameFromApp:(FIRApp *)app {
return [NSString
stringWithFormat:@"projects/%@/apps/%@", app.options.projectID, app.options.googleAppID];
}

// FIREBASE_APP_CHECK_ONLY_END

@end
62 changes: 39 additions & 23 deletions AppCheck/Tests/Unit/Swift/AppCheckAPITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,22 @@ import AppCheck

final class AppCheckAPITests {
func usage() {
let app = FirebaseApp.app()!
let projectID = app.options.projectID!
let resourceName = "projects/\(projectID)/\(app.options.googleAppID)"

// MARK: - AppAttestProvider

#if TARGET_OS_IOS
if #available(iOS 14.0, *) {
if let app = FirebaseApp.app(), let provider = AppAttestProvider(app: app) {
// TODO(andrewheard): Add `requestHooks` in API tests.
if let provider = InternalAppAttestProvider(
storageID: app.name,
resourceName: resourceName,
apiKey: app.options.apiKey,
keychainAccessGroup: nil,
requestHooks: nil
) {
provider.getToken { token, error in
// ...
}
Expand Down Expand Up @@ -90,33 +101,38 @@ final class AppCheckAPITests {
// MARK: - `AppCheckDebugProvider`

// `AppCheckDebugProvider` initializer
if let app = FirebaseApp.app(), let debugProvider = InternalAppCheckDebugProvider(app: app) {
// Get token
debugProvider.getToken { token, error in
if let _ /* error */ = error {
// ...
} else if let _ /* token */ = token {
// ...
}
// TODO(andrewheard): Add `requestHooks` in API tests.
let debugProvider = InternalAppCheckDebugProvider(
storageID: app.name,
resourceName: resourceName,
apiKey: app.options.apiKey,
requestHooks: nil
)
// Get token
debugProvider.getToken { token, error in
if let _ /* error */ = error {
// ...
} else if let _ /* token */ = token {
// ...
}
}

// Get token (async/await)
#if compiler(>=5.5.2) && canImport(_Concurrency)
if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
// async/await is a Swift 5.5+ feature available on iOS 15+
Task {
do {
_ = try await debugProvider.getToken()
} catch {
// ...
}
// Get token (async/await)
#if compiler(>=5.5.2) && canImport(_Concurrency)
if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
// async/await is a Swift 5.5+ feature available on iOS 15+
Task {
do {
_ = try await debugProvider.getToken()
} catch {
// ...
}
}
#endif // compiler(>=5.5.2) && canImport(_Concurrency)
}
#endif // compiler(>=5.5.2) && canImport(_Concurrency)

_ = debugProvider.localDebugToken()
_ = debugProvider.currentDebugToken()
}
_ = debugProvider.localDebugToken()
_ = debugProvider.currentDebugToken()

// MARK: - AppCheckToken

Expand Down