@@ -100,6 +100,8 @@ export interface CredentialsProvider {
100
100
* Specifies a listener to be notified of credential changes
101
101
* (sign-in / sign-out, token changes). It is immediately called once with the
102
102
* initial user.
103
+ *
104
+ * The change listener is invoked on the provided AsyncQueue.
103
105
*/
104
106
setChangeListener (
105
107
asyncQueue : AsyncQueue ,
@@ -135,7 +137,10 @@ export class EmptyCredentialsProvider implements CredentialsProvider {
135
137
) ;
136
138
this . changeListener = changeListener ;
137
139
// Fire with initial user.
138
- changeListener ( User . UNAUTHENTICATED ) ;
140
+ asyncQueue . enqueueRetryable ( ( ) => {
141
+ changeListener ( User . FIRST_PARTY ) ;
142
+ return Promise . resolve ( ) ;
143
+ } ) ;
139
144
}
140
145
141
146
removeChangeListener ( ) : void {
@@ -152,7 +157,21 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
152
157
153
158
/** Tracks the current User. */
154
159
private currentUser : User = User . UNAUTHENTICATED ;
155
- private receivedInitialUser : boolean = false ;
160
+
161
+ /**
162
+ * Promise that allows blocking on the next tokenChange event. The Promise is
163
+ * re-assgined in `awaitTokenAndRaiseInitialEvent()` to allow blocking on
164
+ * an a "lazily" loaded Auth instance. In this case, `this.receivedUser`
165
+ * resolves first when the SDK first detects that there is no synchronous
166
+ * auth, and then gets re-assigned and re-resolved when auth is loaded.
167
+ */
168
+ private receivedUser = new Deferred ( ) ;
169
+
170
+ /**
171
+ * Whether the initial token even has been raised. This can go back to `false`
172
+ * if Firestore first starts without Auth and Auth is loaded later.
173
+ */
174
+ private initialEventRaised : boolean = false ;
156
175
157
176
/**
158
177
* Counter used to detect if the token changed while a getToken request was
@@ -169,28 +188,27 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
169
188
170
189
private asyncQueue : AsyncQueue | null = null ;
171
190
172
- private authListeners : Array < Deferred > = [ ] ;
173
-
174
191
constructor ( authProvider : Provider < FirebaseAuthInternalName > ) {
175
192
this . tokenListener = ( ) => {
176
193
this . tokenCounter ++ ;
177
- this . authListeners . forEach ( listener => listener . resolve ( ) ) ;
194
+ this . receivedUser . resolve ( ) ;
178
195
this . currentUser = this . getUser ( ) ;
179
- if ( this . receivedInitialUser && this . changeListener ) {
196
+ if ( this . initialEventRaised && this . changeListener ) {
197
+ // We only invoke the change listener here if the initial event has been
198
+ // raised. The initial event itself is invoked synchronously in
199
+ // `awaitTokenAndRaiseInitialEvent()`.
180
200
this . changeListener ( this . currentUser ) ;
181
201
}
182
- this . receivedInitialUser = true ;
183
202
} ;
184
203
185
204
this . tokenCounter = 0 ;
186
205
187
206
const registerAuth = ( auth : FirebaseAuthInternal ) : void => {
188
207
logDebug ( 'FirebaseCredentialsProvider' , 'Auth detected' ) ;
189
208
this . auth = auth ;
190
- this . receivedInitialUser = false ;
191
209
// tokenListener can be removed by removeChangeListener()
192
210
if ( this . tokenListener ) {
193
- this . awaitToken ( ) ;
211
+ this . awaitTokenAndRaiseInitialEvent ( ) ;
194
212
this . auth . addAuthTokenListener ( this . tokenListener ) ;
195
213
}
196
214
} ;
@@ -214,7 +232,7 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
214
232
}
215
233
} , 0 ) ;
216
234
217
- this . awaitToken ( ) ;
235
+ this . awaitTokenAndRaiseInitialEvent ( ) ;
218
236
}
219
237
220
238
getToken ( ) : Promise < Token | null > {
@@ -272,13 +290,6 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
272
290
) ;
273
291
this . changeListener = changeListener ;
274
292
this . asyncQueue = asyncQueue ;
275
-
276
- // Fire the initial event
277
- if ( this . receivedInitialUser ) {
278
- asyncQueue . enqueueRetryable ( ( ) => {
279
- changeListener ( this . currentUser ) ;
280
- } ) ;
281
- }
282
293
}
283
294
284
295
removeChangeListener ( ) : void {
@@ -305,20 +316,29 @@ export class FirebaseCredentialsProvider implements CredentialsProvider {
305
316
/**
306
317
* Blocks the AsyncQueue until the next user is available. This is invoked
307
318
* on SDK start to wait for the first user token (or `null` if Auth is not yet
308
- * loaded). If Auth is loaded after Firestore, `awaitToken()` is also used to
309
- * block Firestore until auth is fully initialized.
319
+ * loaded). If Auth is loaded after Firestore,
320
+ * `awaitTokenAndRaiseInitialEvent()` is also used to block Firestore until
321
+ * Auth is fully initialized.
322
+ *
323
+ * This function also invokes the change listener synchronously once a token
324
+ * is available.
310
325
*/
311
- private awaitToken ( ) : void {
326
+ private awaitTokenAndRaiseInitialEvent ( ) : void {
327
+ this . initialEventRaised = false ;
312
328
if ( this . asyncQueue ) {
313
- this . authListeners . push ( new Deferred < void > ( ) ) ;
329
+ // Create a new deferred Promise that gets resolved when we receive the
330
+ // next token. Ensure that all previous Promises also get resolved.
331
+ const awaitToken = new Deferred < void > ( ) ;
332
+ awaitToken . promise . then ( ( ) => awaitToken . resolve ( ) ) ;
333
+ this . receivedUser = awaitToken ;
314
334
this . asyncQueue . enqueueRetryable ( async ( ) => {
315
- console . log ( 'Blocking queue' ) ;
316
- await Promise . all ( this . authListeners . map ( deferred => deferred . promise ) ) ;
335
+ await awaitToken . promise ;
317
336
if ( this . changeListener ) {
318
337
this . changeListener ( this . currentUser ) ;
319
338
}
320
339
} ) ;
321
340
}
341
+ this . initialEventRaised = true ;
322
342
}
323
343
}
324
344
@@ -387,7 +407,10 @@ export class FirstPartyCredentialsProvider implements CredentialsProvider {
387
407
changeListener : CredentialChangeListener
388
408
) : void {
389
409
// Fire with initial uid.
390
- changeListener ( User . FIRST_PARTY ) ;
410
+ asyncQueue . enqueueRetryable ( ( ) => {
411
+ changeListener ( User . FIRST_PARTY ) ;
412
+ return Promise . resolve ( ) ;
413
+ } ) ;
391
414
}
392
415
393
416
removeChangeListener ( ) : void { }
0 commit comments