Skip to content

Commit 186591d

Browse files
authored
firebase-ios-sdk/1499: Restart streams on any token change. (#1692)
[Port of firebase/firebase-js-sdk#1120] Fixes #1499. This reworks our "user listener" into a "credential change listener" that fires on any token change, even if the User did not change. This allows us to restart our streams (but not switch mutation queues, etc.) on token changes.
1 parent f7b5869 commit 186591d

15 files changed

+73
-67
lines changed

Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ - (void)runTimer:(FSTTimerID)timerID {
250250
- (void)changeUser:(const User &)user {
251251
_currentUser = user;
252252
[self.dispatchQueue dispatchSync:^{
253-
[self.syncEngine userDidChange:user];
253+
[self.syncEngine credentialDidChangeWithUser:user];
254254
}];
255255
}
256256

Firestore/Source/Core/FSTFirestoreClient.mm

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ - (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo
128128
auto userPromise = std::make_shared<std::promise<User>>();
129129

130130
__weak typeof(self) weakSelf = self;
131-
auto userChangeListener = [initialized = false, userPromise, weakSelf,
132-
workerDispatchQueue](User user) mutable {
131+
auto credentialChangeListener = [initialized = false, userPromise, weakSelf,
132+
workerDispatchQueue](User user) mutable {
133133
typeof(self) strongSelf = weakSelf;
134134
if (!strongSelf) return;
135135

@@ -138,14 +138,14 @@ - (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo
138138
userPromise->set_value(user);
139139
} else {
140140
[workerDispatchQueue dispatchAsync:^{
141-
[strongSelf userDidChange:user];
141+
[strongSelf credentialDidChangeWithUser:user];
142142
}];
143143
}
144144
};
145145

146-
_credentialsProvider->SetUserChangeListener(userChangeListener);
146+
_credentialsProvider->SetCredentialChangeListener(credentialChangeListener);
147147

148-
// Defer initialization until we get the current user from the userChangeListener. This is
148+
// Defer initialization until we get the current user from the credentialChangeListener. This is
149149
// guaranteed to be synchronously dispatched onto our worker queue, so we will be initialized
150150
// before any subsequently queued work runs.
151151
[_workerDispatchQueue dispatchAsync:^{
@@ -159,6 +159,7 @@ - (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo
159159
- (void)initializeWithUser:(const User &)user usePersistence:(BOOL)usePersistence {
160160
// Do all of our initialization on our own dispatch queue.
161161
[self.workerDispatchQueue verifyIsCurrentQueue];
162+
LOG_DEBUG("Initializing. Current user: %s", user.uid());
162163

163164
// Note: The initialization work must all be synchronous (we can't dispatch more work) since
164165
// external write/listen operations could get queued to run before that subsequent work
@@ -213,11 +214,11 @@ - (void)initializeWithUser:(const User &)user usePersistence:(BOOL)usePersistenc
213214
[_remoteStore start];
214215
}
215216

216-
- (void)userDidChange:(const User &)user {
217+
- (void)credentialDidChangeWithUser:(const User &)user {
217218
[self.workerDispatchQueue verifyIsCurrentQueue];
218219

219-
LOG_DEBUG("User Changed: %s", user.uid());
220-
[self.syncEngine userDidChange:user];
220+
LOG_DEBUG("Credential Changed. Current user: %s", user.uid());
221+
[self.syncEngine credentialDidChangeWithUser:user];
221222
}
222223

223224
- (void)applyChangedOnlineState:(FSTOnlineState)onlineState {
@@ -245,7 +246,7 @@ - (void)enableNetworkWithCompletion:(nullable FSTVoidErrorBlock)completion {
245246

246247
- (void)shutdownWithCompletion:(nullable FSTVoidErrorBlock)completion {
247248
[self.workerDispatchQueue dispatchAsync:^{
248-
self->_credentialsProvider->SetUserChangeListener(nullptr);
249+
self->_credentialsProvider->SetCredentialChangeListener(nullptr);
249250

250251
[self.remoteStore shutdown];
251252
[self.persistence shutdown];

Firestore/Source/Core/FSTSyncEngine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ NS_ASSUME_NONNULL_BEGIN
100100
updateBlock:(FSTTransactionBlock)updateBlock
101101
completion:(FSTVoidIDErrorBlock)completion;
102102

103-
- (void)userDidChange:(const firebase::firestore::auth::User &)user;
103+
- (void)credentialDidChangeWithUser:(const firebase::firestore::auth::User &)user;
104104

105105
/** Applies an FSTOnlineState change to the sync engine and notifies any views of the change. */
106106
- (void)applyChangedOnlineState:(FSTOnlineState)onlineState;

Firestore/Source/Core/FSTSyncEngine.mm

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -560,15 +560,18 @@ - (void)removeLimboTargetForKey:(const DocumentKey &)key {
560560
return _limboTargetsByKey;
561561
}
562562

563-
- (void)userDidChange:(const User &)user {
563+
- (void)credentialDidChangeWithUser:(const firebase::firestore::auth::User &)user {
564+
BOOL userChanged = (_currentUser != user);
564565
_currentUser = user;
565566

566-
// Notify local store and emit any resulting events from swapping out the mutation queue.
567-
FSTMaybeDocumentDictionary *changes = [self.localStore userDidChange:user];
568-
[self emitNewSnapshotsWithChanges:changes remoteEvent:nil];
567+
if (userChanged) {
568+
// Notify local store and emit any resulting events from swapping out the mutation queue.
569+
FSTMaybeDocumentDictionary *changes = [self.localStore userDidChange:user];
570+
[self emitNewSnapshotsWithChanges:changes remoteEvent:nil];
571+
}
569572

570573
// Notify remote store so it can restart its streams.
571-
[self.remoteStore userDidChange:user];
574+
[self.remoteStore credentialDidChange];
572575
}
573576

574577
- (firebase::firestore::model::DocumentKeySet)remoteKeysForTarget:(FSTBoxedTargetID *)targetId {

Firestore/Source/Remote/FSTRemoteStore.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ NS_ASSUME_NONNULL_BEGIN
129129
* In response the remote store tears down streams and clears up any tracked operations that should
130130
* not persist across users. Restarts the streams if appropriate.
131131
*/
132-
- (void)userDidChange:(const firebase::firestore::auth::User &)user;
132+
- (void)credentialDidChange;
133133

134134
/** Listens to the target identified by the given FSTQueryData. */
135135
- (void)listenToTargetWithQueryData:(FSTQueryData *)queryData;

Firestore/Source/Remote/FSTRemoteStore.mm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,12 @@ - (void)shutdown {
209209
[self.onlineStateTracker updateState:FSTOnlineStateUnknown];
210210
}
211211

212-
- (void)userDidChange:(const User &)user {
213-
LOG_DEBUG("FSTRemoteStore %s changing users: %s", (__bridge void *)self, user.uid());
212+
- (void)credentialDidChange {
214213
if ([self isNetworkEnabled]) {
215214
// Tear down and re-create our network streams. This will ensure we get a fresh auth token
216215
// for the new user and re-fill the write pipeline with new mutations from the LocalStore
217216
// (since mutations are per-user).
217+
LOG_DEBUG("FSTRemoteStore %s restarting streams for new credential", (__bridge void *)self);
218218
[self disableNetworkInternal];
219219
[self.onlineStateTracker updateState:FSTOnlineStateUnknown];
220220
[self enableNetwork];

Firestore/core/src/firebase/firestore/auth/credentials_provider.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace firebase {
2020
namespace firestore {
2121
namespace auth {
2222

23-
CredentialsProvider::CredentialsProvider() : user_change_listener_(nullptr) {
23+
CredentialsProvider::CredentialsProvider() : change_listener_(nullptr) {
2424
}
2525

2626
CredentialsProvider::~CredentialsProvider() {

Firestore/core/src/firebase/firestore/auth/credentials_provider.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ namespace auth {
3333
// `TokenErrorListener` is a listener that gets a token or an error.
3434
using TokenListener = std::function<void(util::StatusOr<Token>)>;
3535

36-
// Listener notified with a User change.
37-
using UserChangeListener = std::function<void(User user)>;
36+
// Listener notified with a credential change.
37+
using CredentialChangeListener = std::function<void(User user)>;
3838

3939
/**
4040
* Provides methods for getting the uid and token for the current user and
@@ -56,22 +56,24 @@ class CredentialsProvider {
5656
virtual void InvalidateToken() = 0;
5757

5858
/**
59-
* Sets the listener to be notified of user changes (sign-in / sign-out). It
60-
* is immediately called once with the initial user.
59+
* Sets the listener to be notified of credential changes (sign-in /
60+
* sign-out, token changes). It is immediately called once with the initial
61+
* user.
6162
*
6263
* Call with nullptr to remove previous listener.
6364
*/
64-
virtual void SetUserChangeListener(UserChangeListener listener) = 0;
65+
virtual void SetCredentialChangeListener(
66+
CredentialChangeListener changeListener) = 0;
6567

6668
protected:
6769
/**
68-
* A listener to be notified of user changes (sign-in / sign-out). It is
69-
* immediately called once with the initial user.
70+
* A listener to be notified of credential changes (sign-in / sign-out, token
71+
* changes). It is immediately called once with the initial user.
7072
*
7173
* Note that this block will be called back on an arbitrary thread that is not
7274
* the normal Firestore worker thread.
7375
*/
74-
UserChangeListener user_change_listener_;
76+
CredentialChangeListener change_listener_;
7577
};
7678

7779
} // namespace auth

Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ void EmptyCredentialsProvider::GetToken(TokenListener completion) {
2828
}
2929
}
3030

31-
void EmptyCredentialsProvider::SetUserChangeListener(
32-
UserChangeListener listener) {
33-
if (listener) {
34-
listener(User::Unauthenticated());
31+
void EmptyCredentialsProvider::SetCredentialChangeListener(
32+
CredentialChangeListener changeListener) {
33+
if (changeListener) {
34+
changeListener(User::Unauthenticated());
3535
}
3636
}
3737

Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class EmptyCredentialsProvider : public CredentialsProvider {
2828
public:
2929
void GetToken(TokenListener completion) override;
3030
void InvalidateToken() override;
31-
void SetUserChangeListener(UserChangeListener listener) override;
31+
void SetCredentialChangeListener(
32+
CredentialChangeListener changeListener) override;
3233
};
3334

3435
} // namespace auth

Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ namespace auth {
4343
* `FirebaseCredentialsProvider` uses Firebase Auth via `FIRApp` to get an auth
4444
* token.
4545
*
46-
* NOTE: To simplify the implementation, it requires that you set
47-
* `userChangeListener` with a non-`nil` value no more than once and don't call
48-
* `getTokenForcingRefresh:` after setting it to `nil`.
46+
* NOTE: To simplify the implementation, it requires that you call
47+
* `SetCredentialChangeListener()` with a non-nullptr value no more than once
48+
* and don't call `GetToken()` after setting it to `nullptr`.
4949
*
5050
* This class must be implemented in a thread-safe manner since it is accessed
5151
* from the thread backing our internal worker queue and the callbacks from
@@ -70,7 +70,8 @@ class FirebaseCredentialsProvider : public CredentialsProvider {
7070

7171
void GetToken(TokenListener completion) override;
7272

73-
void SetUserChangeListener(UserChangeListener listener) override;
73+
void SetCredentialChangeListener(
74+
CredentialChangeListener changeListener) override;
7475

7576
void InvalidateToken() override;
7677

@@ -96,10 +97,10 @@ class FirebaseCredentialsProvider : public CredentialsProvider {
9697
User current_user;
9798

9899
/**
99-
* Counter used to detect if the user changed while a
100-
* -getTokenForcingRefresh: request was outstanding.
100+
* Counter used to detect if the token changed while a GetToken() request
101+
* was outstanding.
101102
*/
102-
int user_counter = 0;
103+
int token_counter = 0;
103104

104105
std::mutex mutex;
105106

Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,11 @@
6161

6262
NSString* user_id =
6363
user_info[FIRAuthStateDidChangeInternalNotificationUIDKey];
64-
User new_user = User::FromUid(user_id);
65-
if (new_user != contents->current_user) {
66-
contents->current_user = new_user;
67-
contents->user_counter++;
68-
UserChangeListener listener = user_change_listener_;
69-
if (listener) {
70-
listener(contents->current_user);
71-
}
64+
contents->current_user = User::FromUid(user_id);
65+
contents->token_counter++;
66+
CredentialChangeListener listener = change_listener_;
67+
if (listener) {
68+
listener(contents->current_user);
7269
}
7370
}];
7471
}
@@ -86,9 +83,9 @@
8683
HARD_ASSERT(auth_listener_handle_,
8784
"GetToken cannot be called after listener removed.");
8885

89-
// Take note of the current value of the userCounter so that this method can
90-
// fail if there is a user change while the request is outstanding.
91-
int initial_user_counter = contents_->user_counter;
86+
// Take note of the current value of the tokenCounter so that this method can
87+
// fail if there is a token change while the request is outstanding.
88+
int initial_token_counter = contents_->token_counter;
9289

9390
std::weak_ptr<Contents> weak_contents = contents_;
9491
void (^get_token_callback)(NSString*, NSError*) = ^(
@@ -99,12 +96,12 @@
9996
}
10097

10198
std::unique_lock<std::mutex> lock(contents->mutex);
102-
if (initial_user_counter != contents->user_counter) {
99+
if (initial_token_counter != contents->token_counter) {
103100
// Cancel the request since the user changed while the request was
104101
// outstanding so the response is likely for a previous user (which
105102
// user, we can't be sure).
106103
completion(util::Status(FirestoreErrorCode::Aborted,
107-
"getToken aborted due to user change."));
104+
"getToken aborted due to token change."));
108105
} else {
109106
if (error == nil) {
110107
if (token != nil) {
@@ -139,21 +136,20 @@
139136
contents_->force_refresh = true;
140137
}
141138

142-
void FirebaseCredentialsProvider::SetUserChangeListener(
143-
UserChangeListener listener) {
139+
void FirebaseCredentialsProvider::SetCredentialChangeListener(
140+
CredentialChangeListener changeListener) {
144141
std::unique_lock<std::mutex> lock(contents_->mutex);
145-
if (listener) {
146-
HARD_ASSERT(!user_change_listener_, "set user_change_listener twice!");
142+
if (changeListener) {
143+
HARD_ASSERT(!change_listener_, "set change_listener twice!");
147144
// Fire initial event.
148-
listener(contents_->current_user);
145+
changeListener(contents_->current_user);
149146
} else {
150-
HARD_ASSERT(auth_listener_handle_, "removed user_change_listener twice!");
151-
HARD_ASSERT(user_change_listener_,
152-
"user_change_listener removed without being set!");
147+
HARD_ASSERT(auth_listener_handle_, "removed change_listener twice!");
148+
HARD_ASSERT(change_listener_, "change_listener removed without being set!");
153149
[[NSNotificationCenter defaultCenter] removeObserver:auth_listener_handle_];
154150
auth_listener_handle_ = nil;
155151
}
156-
user_change_listener_ = listener;
152+
change_listener_ = changeListener;
157153
}
158154

159155
} // namespace auth

Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ TEST(CredentialsProvider, Typedef) {
3636
EXPECT_EQ(nullptr, token_listener);
3737
EXPECT_FALSE(token_listener);
3838

39-
UserChangeListener user_change_listener = [](User user) { UNUSED(user); };
39+
CredentialChangeListener user_change_listener = [](User user) {
40+
UNUSED(user);
41+
};
4042
EXPECT_NE(nullptr, user_change_listener);
4143
EXPECT_TRUE(user_change_listener);
4244

Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ TEST(EmptyCredentialsProvider, GetToken) {
3737

3838
TEST(EmptyCredentialsProvider, SetListener) {
3939
EmptyCredentialsProvider credentials_provider;
40-
credentials_provider.SetUserChangeListener([](User user) {
40+
credentials_provider.SetCredentialChangeListener([](User user) {
4141
EXPECT_EQ("", user.uid());
4242
EXPECT_FALSE(user.is_authenticated());
4343
});
4444

45-
credentials_provider.SetUserChangeListener(nullptr);
45+
credentials_provider.SetCredentialChangeListener(nullptr);
4646
}
4747

4848
TEST(EmptyCredentialsProvider, InvalidateToken) {

Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,12 @@ - (void)getTokenForcingRefresh:(BOOL)forceRefresh
134134
FSTAuthFake* auth =
135135
[[FSTAuthFake alloc] initWithToken:@"default token" uid:@"fake uid"];
136136
FirebaseCredentialsProvider credentials_provider(app, auth);
137-
credentials_provider.SetUserChangeListener([](User user) {
137+
credentials_provider.SetCredentialChangeListener([](User user) {
138138
EXPECT_EQ("fake uid", user.uid());
139139
EXPECT_TRUE(user.is_authenticated());
140140
});
141141

142-
credentials_provider.SetUserChangeListener(nullptr);
142+
credentials_provider.SetCredentialChangeListener(nullptr);
143143
}
144144

145145
TEST(FirebaseCredentialsProviderTest, InvalidateToken) {

0 commit comments

Comments
 (0)