Skip to content

Commit c380832

Browse files
authored
Token-changed listeners for iOS AppCheck (#1172)
1 parent 3c64b33 commit c380832

File tree

4 files changed

+153
-4
lines changed

4 files changed

+153
-4
lines changed

app_check/integration_test/src/integration_test.cc

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,22 @@ class FirebaseAppCheckTest : public FirebaseTest {
119119
std::vector<firebase::database::DatabaseReference> cleanup_paths_;
120120
};
121121

122+
// Listens for token changed notifications
123+
class TestAppCheckListener : public firebase::app_check::AppCheckListener {
124+
public:
125+
TestAppCheckListener() : num_token_changes_(0) {}
126+
~TestAppCheckListener() override {}
127+
128+
void OnAppCheckTokenChanged(
129+
const firebase::app_check::AppCheckToken& token) override {
130+
last_token_ = token;
131+
num_token_changes_ += 1;
132+
}
133+
134+
int num_token_changes_;
135+
firebase::app_check::AppCheckToken last_token_;
136+
};
137+
122138
// Initialization flow looks like this:
123139
// - For each test:
124140
// - Optionally initialize App Check.
@@ -133,7 +149,14 @@ void FirebaseAppCheckTest::InitializeAppCheckWithDebug() {
133149
firebase::app_check::DebugAppCheckProviderFactory::GetInstance());
134150
}
135151

136-
void FirebaseAppCheckTest::TerminateAppCheck() {}
152+
void FirebaseAppCheckTest::TerminateAppCheck() {
153+
::firebase::app_check::AppCheck* app_check =
154+
::firebase::app_check::AppCheck::GetInstance(app_);
155+
if (app_check) {
156+
LogDebug("Shutdown App Check.");
157+
delete app_check;
158+
}
159+
}
137160

138161
void FirebaseAppCheckTest::InitializeApp() {
139162
LogDebug("Initialize Firebase App.");
@@ -178,8 +201,8 @@ void FirebaseAppCheckTest::TearDown() {
178201
// Teardown all the products
179202
TerminateDatabase();
180203
TerminateAuth();
181-
TerminateApp();
182204
TerminateAppCheck();
205+
TerminateApp();
183206
FirebaseTest::TearDown();
184207
}
185208

@@ -380,6 +403,45 @@ TEST_F(FirebaseAppCheckTest, TestGetTokenLastResult) {
380403
future2.result()->expire_time_millis);
381404
}
382405

406+
TEST_F(FirebaseAppCheckTest, TestAddTokenChangedListener) {
407+
InitializeAppCheckWithDebug();
408+
InitializeApp();
409+
::firebase::app_check::AppCheck* app_check =
410+
::firebase::app_check::AppCheck::GetInstance(app_);
411+
ASSERT_NE(app_check, nullptr);
412+
413+
// Create and add a token changed listener.
414+
TestAppCheckListener token_changed_listener;
415+
app_check->AddAppCheckListener(&token_changed_listener);
416+
417+
firebase::Future<::firebase::app_check::AppCheckToken> future =
418+
app_check->GetAppCheckToken(true);
419+
EXPECT_TRUE(WaitForCompletion(future, "GetToken"));
420+
::firebase::app_check::AppCheckToken token = *future.result();
421+
422+
ASSERT_EQ(token_changed_listener.num_token_changes_, 1);
423+
EXPECT_EQ(token_changed_listener.last_token_.token, token.token);
424+
}
425+
426+
TEST_F(FirebaseAppCheckTest, TestRemoveTokenChangedListener) {
427+
InitializeAppCheckWithDebug();
428+
InitializeApp();
429+
::firebase::app_check::AppCheck* app_check =
430+
::firebase::app_check::AppCheck::GetInstance(app_);
431+
ASSERT_NE(app_check, nullptr);
432+
433+
// Create, add, and immediately remove a token changed listener.
434+
TestAppCheckListener token_changed_listener;
435+
app_check->AddAppCheckListener(&token_changed_listener);
436+
app_check->RemoveAppCheckListener(&token_changed_listener);
437+
438+
firebase::Future<::firebase::app_check::AppCheckToken> future =
439+
app_check->GetAppCheckToken(true);
440+
EXPECT_TRUE(WaitForCompletion(future, "GetToken"));
441+
442+
ASSERT_EQ(token_changed_listener.num_token_changes_, 0);
443+
}
444+
383445
TEST_F(FirebaseAppCheckTest, TestSignIn) {
384446
InitializeAppCheckWithDebug();
385447
InitializeApp();

app_check/src/include/firebase/app_check.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ struct AppCheckToken {
5858

5959
/// @brief Base class used to receive messages when AppCheck token changes.
6060
class AppCheckListener {
61+
public:
6162
virtual ~AppCheckListener() = 0;
6263
/// This method gets invoked on the UI thread on changes to the token state.
6364
/// Does not trigger on token expiry.

app_check/src/ios/app_check_ios.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,32 @@
2626
#import "FIRAppCheck.h"
2727
#endif // __OBJC__
2828

29+
#ifdef __OBJC__
30+
// Interacts with the default notification center.
31+
@interface AppCheckNotificationCenterWrapper : NSObject
32+
33+
- (id)init;
34+
35+
- (void)stopListening;
36+
37+
- (void)addListener:(firebase::app_check::AppCheckListener* _Nonnull)listener;
38+
39+
- (void)removeListener:(firebase::app_check::AppCheckListener* _Nonnull)listener;
40+
41+
- (void)appCheckTokenDidChangeNotification:(NSNotification*)notification;
42+
43+
@end
44+
#endif // __OBJC__
45+
2946
namespace firebase {
3047
namespace app_check {
3148
namespace internal {
3249

50+
// This defines the class AppCheckNotificationCenterWrapperPointer, which is a
51+
// C++-compatible wrapper around the AppCheckNotificationCenterWrapper Obj-C
52+
// class.
53+
OBJ_C_PTR_WRAPPER(AppCheckNotificationCenterWrapper);
54+
3355
// This defines the class FIRAppCheckPointer, which is a C++-compatible
3456
// wrapper around the FIRAppCheck Obj-C class.
3557
OBJ_C_PTR_WRAPPER(FIRAppCheck);
@@ -61,10 +83,16 @@ class AppCheckInternal {
6183
private:
6284
#ifdef __OBJC__
6385
FIRAppCheck* impl() const { return impl_->get(); }
86+
87+
AppCheckNotificationCenterWrapper* notification_center_wrapper() const {
88+
return notification_center_wrapper_->get();
89+
}
6490
#endif // __OBJC__
6591

6692
UniquePtr<FIRAppCheckPointer> impl_;
6793

94+
UniquePtr<AppCheckNotificationCenterWrapperPointer> notification_center_wrapper_;
95+
6896
::firebase::App* app_;
6997

7098
FutureManager future_manager_;

app_check/src/ios/app_check_ios.mm

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "app_check/src/ios/app_check_ios.h"
1616

1717
#import "FIRApp.h"
18+
#import "FIRAppCheck.h"
1819
#import "FIRAppCheckProvider.h"
1920
#import "FIRAppCheckProviderFactory.h"
2021
#import "FIRAppCheckToken.h"
@@ -91,16 +92,69 @@ - (id)initWithProviderFactory:(firebase::app_check::AppCheckProviderFactory* _No
9192

9293
@end
9394

95+
@implementation AppCheckNotificationCenterWrapper
96+
97+
// A collection of NSValues containing AppCheckListener*
98+
NSMutableArray* listeners_;
99+
100+
- (id)init {
101+
self = [super init];
102+
if (self) {
103+
listeners_ = [[NSMutableArray alloc] init];
104+
[[NSNotificationCenter defaultCenter] addObserver:self
105+
selector:@selector(appCheckTokenDidChangeNotification:)
106+
name:FIRAppCheckAppCheckTokenDidChangeNotification
107+
object:nil];
108+
}
109+
return self;
110+
}
111+
112+
- (void)stopListening {
113+
[[NSNotificationCenter defaultCenter] removeObserver:self
114+
name:FIRAppCheckAppCheckTokenDidChangeNotification
115+
object:nil];
116+
}
117+
118+
- (void)addListener:(firebase::app_check::AppCheckListener* _Nonnull)listener {
119+
[listeners_ addObject:[NSValue valueWithPointer:listener]];
120+
}
121+
122+
- (void)removeListener:(firebase::app_check::AppCheckListener* _Nonnull)listener {
123+
[listeners_ removeObject:[NSValue valueWithPointer:listener]];
124+
}
125+
126+
- (void)appCheckTokenDidChangeNotification:(NSNotification*)notification {
127+
NSDictionary* userInfo = notification.userInfo;
128+
NSString* app_name = (NSString*)userInfo[kFIRAppCheckAppNameNotificationKey];
129+
NSString* token = (NSString*)userInfo[kFIRAppCheckTokenNotificationKey];
130+
// TODO(b/263138261): Include expiration time in this app check token.
131+
// Note: The notification contains a token string, but does not currently
132+
// contain expiration time.
133+
firebase::app_check::AppCheckToken cpp_token;
134+
cpp_token.token = firebase::util::NSStringToString(token);
135+
136+
for (NSValue* listener_value in listeners_) {
137+
firebase::app_check::AppCheckListener* listener =
138+
static_cast<firebase::app_check::AppCheckListener*>(listener_value.pointerValue);
139+
listener->OnAppCheckTokenChanged(cpp_token);
140+
}
141+
}
142+
143+
@end
144+
94145
namespace firebase {
95146
namespace app_check {
96147
namespace internal {
97148

98149
AppCheckInternal::AppCheckInternal(App* app) : app_(app) {
99150
future_manager().AllocFutureApi(this, kAppCheckFnCount);
100151
impl_ = MakeUnique<FIRAppCheckPointer>([FIRAppCheck appCheck]);
152+
notification_center_wrapper_ = MakeUnique<AppCheckNotificationCenterWrapperPointer>(
153+
[[AppCheckNotificationCenterWrapper alloc] init]);
101154
}
102155

103156
AppCheckInternal::~AppCheckInternal() {
157+
[notification_center_wrapper() stopListening];
104158
future_manager().ReleaseFutureApi(this);
105159
app_ = nullptr;
106160
}
@@ -157,9 +211,13 @@ - (id)initWithProviderFactory:(firebase::app_check::AppCheckProviderFactory* _No
157211
future()->LastResult(kAppCheckFnGetAppCheckToken));
158212
}
159213

160-
void AppCheckInternal::AddAppCheckListener(AppCheckListener* listener) {}
214+
void AppCheckInternal::AddAppCheckListener(AppCheckListener* listener) {
215+
[notification_center_wrapper() addListener:listener];
216+
}
161217

162-
void AppCheckInternal::RemoveAppCheckListener(AppCheckListener* listener) {}
218+
void AppCheckInternal::RemoveAppCheckListener(AppCheckListener* listener) {
219+
[notification_center_wrapper() removeListener:listener];
220+
}
163221

164222
} // namespace internal
165223
} // namespace app_check

0 commit comments

Comments
 (0)