Skip to content

Commit e47a26e

Browse files
authored
Implement main App Check classes for iOS (#1149)
Implement main app check logic for iOS. Add app check integration test cases to set factory and get token.
1 parent 5d62222 commit e47a26e

File tree

7 files changed

+281
-15
lines changed

7 files changed

+281
-15
lines changed

app/src/app_ios.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define FIREBASE_APP_SRC_APP_IOS_H_
1919
#include "FIRApp.h"
2020
#include "FIRConfiguration.h"
21+
#include "app/src/include/firebase/app.h"
2122
#include "app/src/util_ios.h"
2223

2324
namespace firebase {
@@ -27,6 +28,18 @@ OBJ_C_PTR_WRAPPER_NAMED(AppInternal, FIRApp);
2728

2829
void SetFirConfigurationLoggerLevel(FIRLoggerLevel level);
2930

31+
// Allows lookup of App by name for apps which are not yet fully initialized.
32+
// These Apps should have a name and options, but will not yet have an
33+
// associated AppInternal. This is used by AppCheck to use the App during
34+
// initialization of AppCheck while the internal FIRApp is being configured.
35+
App* FindPartialAppByName(const char* name);
36+
37+
// Enables lookup by name for a partially initialized App.
38+
void AddPartialApp(App*);
39+
40+
// Disables lookup by name for a partially initialized App.
41+
void RemovePartialApp(App*);
42+
3043
} // namespace internal
3144

3245
} // namespace firebase

app/src/app_ios.mm

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,22 @@ static void PlatformOptionsToAppOptions(FIROptions* platform_options, AppOptions
253253
return app;
254254
}
255255
LogDebug("Creating Firebase App %s for %s", name, kFirebaseVersionString);
256+
app = new App();
257+
app->options_ = options;
258+
app->name_ = name;
259+
260+
// Add the app to a temporary map in case app check initialization
261+
// needs to reference the app before it is fully initialized.
262+
internal::AddPartialApp(app);
256263
FIRApp* platform_app = CreateOrGetPlatformApp(options, name);
264+
// Once the app is initialized, the entry in the temporary map is not needed.
265+
internal::RemovePartialApp(app);
257266
if (platform_app) {
258-
app = new App();
259-
app->options_ = options;
260-
app->name_ = name;
261267
app->internal_ = new internal::AppInternal(platform_app);
262268
app_common::AddApp(app, &app->init_results_);
269+
} else {
270+
delete app;
271+
app = nullptr;
263272
}
264273
return app;
265274
}
@@ -286,7 +295,15 @@ static void PlatformOptionsToAppOptions(FIROptions* platform_options, AppOptions
286295
return GetPlatformApp().isDataCollectionDefaultEnabled ? true : false;
287296
}
288297

289-
FIRApp* App::GetPlatformApp() const { return internal_->get(); }
298+
FIRApp* App::GetPlatformApp() const {
299+
if (internal_) {
300+
return internal_->get();
301+
} else {
302+
// AppCheck initialization can depend on the FIRApp associated with an App
303+
// before internal_ has been created.
304+
return GetPlatformAppByName(name_.c_str());
305+
}
306+
}
290307

291308
namespace internal {
292309

@@ -304,6 +321,46 @@ void SetFirConfigurationLoggerLevel(FIRLoggerLevel level) {
304321
g_delayed_fir_configuration_logger_level_set = true;
305322
}
306323
}
307-
} // namespace internal
308324

325+
// Guards g_partial_apps.
326+
static Mutex* g_partial_apps_mutex = new Mutex();
327+
// Stores partially initialized apps, indexed by app name
328+
static std::map<std::string, App*>* g_partial_apps;
329+
330+
App* FindPartialAppByName(const char* name) {
331+
assert(name);
332+
MutexLock lock(*g_partial_apps_mutex);
333+
if (g_partial_apps) {
334+
auto it = g_partial_apps->find(std::string(name));
335+
if (it == g_partial_apps->end()) {
336+
return nullptr;
337+
}
338+
return it->second;
339+
}
340+
return nullptr;
341+
}
342+
343+
void AddPartialApp(App* app) {
344+
MutexLock lock(*g_partial_apps_mutex);
345+
if (!g_partial_apps) {
346+
g_partial_apps = new std::map<std::string, App*>();
347+
}
348+
(*g_partial_apps)[std::string(app->name())] = app;
349+
}
350+
351+
void RemovePartialApp(App* app) {
352+
MutexLock lock(*g_partial_apps_mutex);
353+
if (g_partial_apps) {
354+
auto it = g_partial_apps->find(std::string(app->name()));
355+
if (it != g_partial_apps->end()) {
356+
g_partial_apps->erase(it);
357+
if (g_partial_apps->empty()) {
358+
delete g_partial_apps;
359+
g_partial_apps = nullptr;
360+
}
361+
}
362+
}
363+
}
364+
365+
} // namespace internal
309366
} // namespace firebase

app_check/integration_test/src/integration_test.cc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,52 @@ TEST_F(FirebaseAppCheckTest, TestInitializeAndTerminate) {
334334
InitializeApp();
335335
}
336336

337+
TEST_F(FirebaseAppCheckTest, TestGetTokenForcingRefresh) {
338+
InitializeAppCheckWithDebug();
339+
InitializeApp();
340+
::firebase::app_check::AppCheck* app_check =
341+
::firebase::app_check::AppCheck::GetInstance(app_);
342+
ASSERT_NE(app_check, nullptr);
343+
firebase::Future<::firebase::app_check::AppCheckToken> future =
344+
app_check->GetAppCheckToken(true);
345+
EXPECT_TRUE(WaitForCompletion(future, "GetToken #1"));
346+
::firebase::app_check::AppCheckToken token = *future.result();
347+
EXPECT_NE(token.token, "");
348+
EXPECT_NE(token.expire_time_millis, 0);
349+
350+
// GetToken with force_refresh=false will return the same token.
351+
firebase::Future<::firebase::app_check::AppCheckToken> future2 =
352+
app_check->GetAppCheckToken(false);
353+
EXPECT_TRUE(WaitForCompletion(future2, "GetToken #2"));
354+
EXPECT_EQ(future.result()->expire_time_millis,
355+
future2.result()->expire_time_millis);
356+
357+
// GetToken with force_refresh=true will return a new token.
358+
firebase::Future<::firebase::app_check::AppCheckToken> future3 =
359+
app_check->GetAppCheckToken(true);
360+
EXPECT_TRUE(WaitForCompletion(future3, "GetToken #3"));
361+
EXPECT_NE(future.result()->expire_time_millis,
362+
future3.result()->expire_time_millis);
363+
}
364+
365+
TEST_F(FirebaseAppCheckTest, TestGetTokenLastResult) {
366+
InitializeAppCheckWithDebug();
367+
InitializeApp();
368+
::firebase::app_check::AppCheck* app_check =
369+
::firebase::app_check::AppCheck::GetInstance(app_);
370+
ASSERT_NE(app_check, nullptr);
371+
firebase::Future<::firebase::app_check::AppCheckToken> future =
372+
app_check->GetAppCheckToken(true);
373+
EXPECT_TRUE(WaitForCompletion(future, "GetToken #1"));
374+
375+
firebase::Future<::firebase::app_check::AppCheckToken> future2 =
376+
app_check->GetAppCheckTokenLastResult();
377+
EXPECT_TRUE(WaitForCompletion(future2, "GetTokenLastResult"));
378+
::firebase::app_check::AppCheckToken token = *future2.result();
379+
EXPECT_EQ(future.result()->expire_time_millis,
380+
future2.result()->expire_time_millis);
381+
}
382+
337383
TEST_F(FirebaseAppCheckTest, TestSignIn) {
338384
InitializeAppCheckWithDebug();
339385
InitializeApp();

app_check/src/ios/app_check_ios.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,25 @@
1515
#ifndef FIREBASE_APP_CHECK_SRC_IOS_APP_CHECK_IOS_H_
1616
#define FIREBASE_APP_CHECK_SRC_IOS_APP_CHECK_IOS_H_
1717

18+
#include "app/memory/unique_ptr.h"
1819
#include "app/src/future_manager.h"
1920
#include "app/src/include/firebase/app.h"
2021
#include "app/src/include/firebase/future.h"
22+
#include "app/src/util_ios.h"
2123
#include "app_check/src/include/firebase/app_check.h"
2224

25+
#ifdef __OBJC__
26+
#import "FIRAppCheck.h"
27+
#endif // __OBJC__
28+
2329
namespace firebase {
2430
namespace app_check {
2531
namespace internal {
2632

33+
// This defines the class FIRAppCheckPointer, which is a C++-compatible
34+
// wrapper around the FIRAppCheck Obj-C class.
35+
OBJ_C_PTR_WRAPPER(FIRAppCheck);
36+
2737
class AppCheckInternal {
2838
public:
2939
explicit AppCheckInternal(::firebase::App* app);
@@ -49,6 +59,12 @@ class AppCheckInternal {
4959
ReferenceCountedFutureImpl* future();
5060

5161
private:
62+
#ifdef __OBJC__
63+
FIRAppCheck* impl() const { return impl_->get(); }
64+
#endif // __OBJC__
65+
66+
UniquePtr<FIRAppCheckPointer> impl_;
67+
5268
::firebase::App* app_;
5369

5470
FutureManager future_manager_;

app_check/src/ios/app_check_ios.mm

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,90 @@
1414

1515
#include "app_check/src/ios/app_check_ios.h"
1616

17+
#import "FIRApp.h"
18+
#import "FIRAppCheckProvider.h"
19+
#import "FIRAppCheckProviderFactory.h"
1720
#import "FIRAppCheckToken.h"
1821

22+
#include "app/src/app_common.h"
23+
#include "app/src/app_ios.h"
24+
#include "app/src/util_ios.h"
1925
#include "app_check/src/common/common.h"
26+
#include "app_check/src/ios/util_ios.h"
2027
#include "firebase/app_check.h"
2128

29+
// Defines an iOS AppCheckProvider that wraps a given C++ Provider.
30+
@interface CppAppCheckProvider : NSObject <FIRAppCheckProvider>
31+
32+
@property(nonatomic, nullable) firebase::app_check::AppCheckProvider* cppProvider;
33+
34+
- (id)initWithProvider:(firebase::app_check::AppCheckProvider* _Nonnull)provider;
35+
36+
@end
37+
38+
@implementation CppAppCheckProvider
39+
40+
- (id)initWithProvider:(firebase::app_check::AppCheckProvider* _Nonnull)provider {
41+
self = [super init];
42+
if (self) {
43+
self.cppProvider = provider;
44+
}
45+
return self;
46+
}
47+
48+
- (void)getTokenWithCompletion:(nonnull void (^)(FIRAppCheckToken* _Nullable,
49+
NSError* _Nullable))handler {
50+
auto token_callback{[handler](firebase::app_check::AppCheckToken token, int error_code,
51+
const std::string& error_message) {
52+
NSError* ios_error = firebase::app_check::internal::AppCheckErrorToNSError(
53+
static_cast<firebase::app_check::AppCheckError>(error_code), error_message);
54+
FIRAppCheckToken* ios_token =
55+
firebase::app_check::internal::AppCheckTokenToFIRAppCheckToken(token);
56+
handler(ios_token, ios_error);
57+
}};
58+
_cppProvider->GetToken(token_callback);
59+
}
60+
61+
@end
62+
63+
// Defines an iOS AppCheckProviderFactory that wraps a given C++ Factory.
64+
@interface CppAppCheckProviderFactory : NSObject <FIRAppCheckProviderFactory>
65+
66+
@property(nonatomic, nullable) firebase::app_check::AppCheckProviderFactory* cppProviderFactory;
67+
68+
- (id)initWithProviderFactory:(firebase::app_check::AppCheckProviderFactory* _Nonnull)factory;
69+
70+
@end
71+
72+
@implementation CppAppCheckProviderFactory
73+
74+
- (id)initWithProviderFactory:(firebase::app_check::AppCheckProviderFactory* _Nonnull)factory {
75+
self = [super init];
76+
if (self) {
77+
self.cppProviderFactory = factory;
78+
}
79+
return self;
80+
}
81+
82+
- (nullable id<FIRAppCheckProvider>)createProviderWithApp:(FIRApp*)app {
83+
std::string app_name = firebase::util::NSStringToString(app.name);
84+
firebase::App* cpp_app = firebase::app_common::FindAppByName(app_name.c_str());
85+
if (cpp_app == nullptr) {
86+
cpp_app = firebase::internal::FindPartialAppByName(app_name.c_str());
87+
}
88+
firebase::app_check::AppCheckProvider* cppProvider = _cppProviderFactory->CreateProvider(cpp_app);
89+
return [[CppAppCheckProvider alloc] initWithProvider:cppProvider];
90+
}
91+
92+
@end
93+
2294
namespace firebase {
2395
namespace app_check {
2496
namespace internal {
2597

26-
static AppCheckProviderFactory* g_provider_factory = nullptr;
27-
2898
AppCheckInternal::AppCheckInternal(App* app) : app_(app) {
2999
future_manager().AllocFutureApi(this, kAppCheckFnCount);
100+
impl_ = MakeUnique<FIRAppCheckPointer>([FIRAppCheck appCheck]);
30101
}
31102

32103
AppCheckInternal::~AppCheckInternal() {
@@ -41,15 +112,43 @@
41112
}
42113

43114
void AppCheckInternal::SetAppCheckProviderFactory(AppCheckProviderFactory* factory) {
44-
g_provider_factory = factory;
115+
CppAppCheckProviderFactory* ios_factory =
116+
[[CppAppCheckProviderFactory alloc] initWithProviderFactory:factory];
117+
[FIRAppCheck setAppCheckProviderFactory:ios_factory];
45118
}
46119

47-
void AppCheckInternal::SetTokenAutoRefreshEnabled(bool is_token_auto_refresh_enabled) {}
120+
void AppCheckInternal::SetTokenAutoRefreshEnabled(bool is_token_auto_refresh_enabled) {
121+
impl().isTokenAutoRefreshEnabled = is_token_auto_refresh_enabled;
122+
}
48123

49124
Future<AppCheckToken> AppCheckInternal::GetAppCheckToken(bool force_refresh) {
50-
auto handle = future()->SafeAlloc<AppCheckToken>(kAppCheckFnGetAppCheckToken);
51-
AppCheckToken token;
52-
future()->CompleteWithResult(handle, 0, token);
125+
SafeFutureHandle<AppCheckToken> handle =
126+
future()->SafeAlloc<AppCheckToken>(kAppCheckFnGetAppCheckToken);
127+
128+
// __block allows handle to be referenced inside the objective C completion.
129+
__block SafeFutureHandle<AppCheckToken>* handle_in_block = &handle;
130+
[impl()
131+
tokenForcingRefresh:force_refresh
132+
completion:^(FIRAppCheckToken* _Nullable token, NSError* _Nullable error) {
133+
AppCheckToken cpp_token = AppCheckTokenFromFIRAppCheckToken(token);
134+
if (error != nil) {
135+
NSLog(@"Unable to retrieve App Check token: %@", error);
136+
int error_code = firebase::app_check::internal::AppCheckErrorFromNSError(error);
137+
std::string error_message = util::NSStringToString(error.localizedDescription);
138+
139+
future()->CompleteWithResult(*handle_in_block, error_code, error_message.c_str(),
140+
cpp_token);
141+
return;
142+
}
143+
if (token == nil) {
144+
NSLog(@"App Check token is nil.");
145+
future()->CompleteWithResult(*handle_in_block, kAppCheckErrorUnknown,
146+
"AppCheck GetToken returned an empty token.",
147+
cpp_token);
148+
return;
149+
}
150+
future()->CompleteWithResult(*handle_in_block, kAppCheckErrorNone, cpp_token);
151+
}];
53152
return MakeFuture(future(), handle);
54153
}
55154

app_check/src/ios/util_ios.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,24 @@
1717
#ifndef FIREBASE_APP_CHECK_SRC_IOS_UTIL_IOS_H_
1818
#define FIREBASE_APP_CHECK_SRC_IOS_UTIL_IOS_H_
1919

20+
#include <string>
21+
2022
#import "FIRAppCheckToken.h"
2123
#include "firebase/app_check.h"
2224

2325
namespace firebase {
2426
namespace app_check {
2527
namespace internal {
2628

27-
AppCheckError AppCheckErrorFromNSError(NSError *_Nullable error);
29+
AppCheckError AppCheckErrorFromNSError(NSError* _Nullable error);
30+
31+
NSError* AppCheckErrorToNSError(AppCheckError cpp_error,
32+
const std::string& error_message);
2833

2934
AppCheckToken AppCheckTokenFromFIRAppCheckToken(
30-
FIRAppCheckToken *_Nullable token);
35+
FIRAppCheckToken* _Nullable token);
36+
37+
FIRAppCheckToken* AppCheckTokenToFIRAppCheckToken(AppCheckToken cpp_token);
3138

3239
} // namespace internal
3340
} // namespace app_check

0 commit comments

Comments
 (0)