@@ -78,7 +78,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
78
78
private redirectPersistenceManager ?: PersistenceUserManager ;
79
79
private authStateSubscription = new Subscription < User > ( this ) ;
80
80
private idTokenSubscription = new Subscription < User > ( this ) ;
81
- private beforeStateSubscription = new Subscription < User > ( this ) ;
81
+ private beforeStateQueue : Array < ( user : User | null ) => Promise < void > > = [ ] ;
82
82
private redirectUser : UserInternal | null = null ;
83
83
private isProactiveRefreshEnabled = false ;
84
84
@@ -325,14 +325,26 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
325
325
AuthErrorCode . TENANT_ID_MISMATCH
326
326
) ;
327
327
}
328
+ await this . _runBeforeStateCallbacks ( user ) ;
328
329
329
330
return this . queue ( async ( ) => {
330
331
await this . directlySetCurrentUser ( user as UserInternal | null ) ;
331
332
this . notifyAuthListeners ( ) ;
332
333
} ) ;
333
334
}
334
335
336
+ async _runBeforeStateCallbacks ( user : User | null ) : Promise < void > {
337
+ try {
338
+ for ( const beforeStateCallback of this . beforeStateQueue ) {
339
+ await beforeStateCallback ( user ) ;
340
+ }
341
+ } catch ( e ) {
342
+ throw this . _errorFactory . create ( AuthErrorCode . LOGIN_BLOCKED , { message : e . message } ) ;
343
+ }
344
+ }
345
+
335
346
async signOut ( ) : Promise < void > {
347
+ await this . _runBeforeStateCallbacks ( null ) ;
336
348
// Clear the redirect user when signOut is called
337
349
if ( this . redirectPersistenceManager || this . _popupRedirectResolver ) {
338
350
await this . _setRedirectUser ( null ) ;
@@ -373,14 +385,29 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
373
385
}
374
386
375
387
beforeAuthStateChanged (
376
- next : NextFn < User | null > ,
388
+ callback : ( user : User | null ) => void | Promise < void >
377
389
) : Unsubscribe {
378
- return this . registerStateListener (
379
- this . beforeStateSubscription ,
380
- next ,
381
- error ,
382
- completed
383
- ) ;
390
+ // The callback could be sync or async. Wrap it into a
391
+ // function that is always async.
392
+ const wrappedCallback =
393
+ ( user : User | null ) : Promise < void > => new Promise ( ( resolve , reject ) => {
394
+ try {
395
+ const result = callback ( user ) ;
396
+ // Either resolve with existing promise or wrap a non-promise
397
+ // return value into a promise.
398
+ resolve ( result ) ;
399
+ } catch ( e ) {
400
+ // Sync callback throws.
401
+ reject ( e ) ;
402
+ }
403
+ } ) ;
404
+ this . beforeStateQueue . push ( wrappedCallback ) ;
405
+ const index = this . beforeStateQueue . length - 1 ;
406
+ return ( ) => {
407
+ // Unsubscribe. Replace with no-op. Do not remove from array, or it will disturb
408
+ // indexing of other elements.
409
+ this . beforeStateQueue [ index ] = ( ) => Promise . resolve ( ) ;
410
+ } ;
384
411
}
385
412
386
413
onIdTokenChanged (
@@ -441,7 +468,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
441
468
// Make sure we've cleared any pending persistence actions if we're not in
442
469
// the initializer
443
470
if ( this . _isInitialized ) {
444
- await this . queue ( async ( ) => { } ) ;
471
+ await this . queue ( async ( ) => { } ) ;
445
472
}
446
473
447
474
if ( this . _currentUser ?. _redirectEventId === id ) {
@@ -512,7 +539,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
512
539
completed ?: CompleteFn
513
540
) : Unsubscribe {
514
541
if ( this . _deleted ) {
515
- return ( ) => { } ;
542
+ return ( ) => { } ;
516
543
}
517
544
518
545
const cb =
@@ -620,7 +647,7 @@ class Subscription<T> {
620
647
observer => ( this . observer = observer )
621
648
) ;
622
649
623
- constructor ( readonly auth : AuthInternal ) { }
650
+ constructor ( readonly auth : AuthInternal ) { }
624
651
625
652
get next ( ) : NextFn < T | null > {
626
653
_assert ( this . observer , this . auth , AuthErrorCode . INTERNAL_ERROR ) ;
0 commit comments