From d1992adffbaee4805549fc59b74c441fde51aafd Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 22 Feb 2022 15:51:40 -0800 Subject: [PATCH 1/6] Bookmark todo --- packages/auth/src/core/auth/auth_impl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/auth/src/core/auth/auth_impl.ts b/packages/auth/src/core/auth/auth_impl.ts index b71e4e24bb4..0e342e23819 100644 --- a/packages/auth/src/core/auth/auth_impl.ts +++ b/packages/auth/src/core/auth/auth_impl.ts @@ -528,6 +528,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService { * should only be called from within a queued callback. This is necessary * because the queue shouldn't rely on another queued callback. */ + // TODO: Find where this is called and see if we can run the middleware before it private async directlySetCurrentUser( user: UserInternal | null ): Promise { From 8554836511472ca2af19766f03ac25a866bda4ce Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 28 Feb 2022 14:08:14 -0800 Subject: [PATCH 2/6] wip --- packages/auth/src/core/auth/auth_impl.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/auth/src/core/auth/auth_impl.ts b/packages/auth/src/core/auth/auth_impl.ts index 0e342e23819..f27cb3cdcbd 100644 --- a/packages/auth/src/core/auth/auth_impl.ts +++ b/packages/auth/src/core/auth/auth_impl.ts @@ -78,6 +78,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService { private redirectPersistenceManager?: PersistenceUserManager; private authStateSubscription = new Subscription(this); private idTokenSubscription = new Subscription(this); + private beforeStateSubscription = new Subscription(this); private redirectUser: UserInternal | null = null; private isProactiveRefreshEnabled = false; @@ -371,6 +372,17 @@ export class AuthImpl implements AuthInternal, _FirebaseService { ); } + beforeAuthStateChanged( + next: NextFn, + ): Unsubscribe { + return this.registerStateListener( + this.beforeStateSubscription, + next, + error, + completed + ); + } + onIdTokenChanged( nextOrObserver: NextOrObserver, error?: ErrorFn, From 202a1148de4477f4a674fb9ccdd4418a579d955a Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Wed, 9 Mar 2022 10:07:55 -0800 Subject: [PATCH 3/6] more wip --- packages/auth/src/core/auth/auth_impl.ts | 49 ++++++++++++++++++------ packages/auth/src/core/errors.ts | 6 ++- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/packages/auth/src/core/auth/auth_impl.ts b/packages/auth/src/core/auth/auth_impl.ts index f27cb3cdcbd..c0eeb601612 100644 --- a/packages/auth/src/core/auth/auth_impl.ts +++ b/packages/auth/src/core/auth/auth_impl.ts @@ -78,7 +78,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService { private redirectPersistenceManager?: PersistenceUserManager; private authStateSubscription = new Subscription(this); private idTokenSubscription = new Subscription(this); - private beforeStateSubscription = new Subscription(this); + private beforeStateQueue: Array<(user: User | null) => Promise> = []; private redirectUser: UserInternal | null = null; private isProactiveRefreshEnabled = false; @@ -325,6 +325,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService { AuthErrorCode.TENANT_ID_MISMATCH ); } + await this._runBeforeStateCallbacks(user); return this.queue(async () => { await this.directlySetCurrentUser(user as UserInternal | null); @@ -332,7 +333,18 @@ export class AuthImpl implements AuthInternal, _FirebaseService { }); } + async _runBeforeStateCallbacks(user: User | null): Promise { + try { + for (const beforeStateCallback of this.beforeStateQueue) { + await beforeStateCallback(user); + } + } catch (e) { + throw this._errorFactory.create(AuthErrorCode.LOGIN_BLOCKED, { message: e.message }); + } + } + async signOut(): Promise { + await this._runBeforeStateCallbacks(null); // Clear the redirect user when signOut is called if (this.redirectPersistenceManager || this._popupRedirectResolver) { await this._setRedirectUser(null); @@ -373,14 +385,29 @@ export class AuthImpl implements AuthInternal, _FirebaseService { } beforeAuthStateChanged( - next: NextFn, + callback: (user: User | null) => void | Promise ): Unsubscribe { - return this.registerStateListener( - this.beforeStateSubscription, - next, - error, - completed - ); + // The callback could be sync or async. Wrap it into a + // function that is always async. + const wrappedCallback = + (user: User | null): Promise => new Promise((resolve, reject) => { + try { + const result = callback(user); + // Either resolve with existing promise or wrap a non-promise + // return value into a promise. + resolve(result); + } catch (e) { + // Sync callback throws. + reject(e); + } + }); + this.beforeStateQueue.push(wrappedCallback); + const index = this.beforeStateQueue.length - 1; + return () => { + // Unsubscribe. Replace with no-op. Do not remove from array, or it will disturb + // indexing of other elements. + this.beforeStateQueue[index] = () => Promise.resolve(); + }; } onIdTokenChanged( @@ -441,7 +468,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService { // Make sure we've cleared any pending persistence actions if we're not in // the initializer if (this._isInitialized) { - await this.queue(async () => {}); + await this.queue(async () => { }); } if (this._currentUser?._redirectEventId === id) { @@ -512,7 +539,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService { completed?: CompleteFn ): Unsubscribe { if (this._deleted) { - return () => {}; + return () => { }; } const cb = @@ -620,7 +647,7 @@ class Subscription { observer => (this.observer = observer) ); - constructor(readonly auth: AuthInternal) {} + constructor(readonly auth: AuthInternal) { } get next(): NextFn { _assert(this.observer, this.auth, AuthErrorCode.INTERNAL_ERROR); diff --git a/packages/auth/src/core/errors.ts b/packages/auth/src/core/errors.ts index a140f968da7..332ca35ccc9 100644 --- a/packages/auth/src/core/errors.ts +++ b/packages/auth/src/core/errors.ts @@ -75,6 +75,7 @@ export const enum AuthErrorCode { INVALID_SENDER = 'invalid-sender', INVALID_SESSION_INFO = 'invalid-verification-id', INVALID_TENANT_ID = 'invalid-tenant-id', + LOGIN_BLOCKED = 'login-blocked', MFA_INFO_NOT_FOUND = 'multi-factor-info-not-found', MFA_REQUIRED = 'multi-factor-auth-required', MISSING_ANDROID_PACKAGE_NAME = 'missing-android-pkg-name', @@ -245,6 +246,7 @@ function _debugErrorMap(): ErrorMap { 'The verification ID used to create the phone auth credential is invalid.', [AuthErrorCode.INVALID_TENANT_ID]: "The Auth instance's tenant ID is invalid.", + [AuthErrorCode.LOGIN_BLOCKED]: "Login blocked by user-provided method.", [AuthErrorCode.MISSING_ANDROID_PACKAGE_NAME]: 'An Android Package Name must be provided if the Android App is required to be installed.', [AuthErrorCode.MISSING_AUTH_DOMAIN]: @@ -414,9 +416,10 @@ type GenericAuthErrorParams = { | AuthErrorCode.NO_AUTH_EVENT | AuthErrorCode.OPERATION_NOT_SUPPORTED >]: { - appName: AppName; + appName?: AppName; email?: string; phoneNumber?: string; + message?: string; }; }; @@ -427,6 +430,7 @@ export interface AuthErrorParams extends GenericAuthErrorParams { [AuthErrorCode.ARGUMENT_ERROR]: { appName?: AppName }; [AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH]: { appName?: AppName }; [AuthErrorCode.INTERNAL_ERROR]: { appName?: AppName }; + [AuthErrorCode.LOGIN_BLOCKED]: { message?: string }; [AuthErrorCode.OPERATION_NOT_SUPPORTED]: { appName?: AppName }; [AuthErrorCode.NO_AUTH_EVENT]: { appName?: AppName }; [AuthErrorCode.MFA_REQUIRED]: { From ef9b649066915547a1b1bd2ee76a11c6405a11d5 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 28 Mar 2022 12:13:10 -0700 Subject: [PATCH 4/6] Address PR comments --- packages/auth/src/core/auth/auth_impl.ts | 18 +++++++++++++----- packages/auth/src/core/errors.ts | 4 ++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/auth/src/core/auth/auth_impl.ts b/packages/auth/src/core/auth/auth_impl.ts index c0eeb601612..263b7f611bf 100644 --- a/packages/auth/src/core/auth/auth_impl.ts +++ b/packages/auth/src/core/auth/auth_impl.ts @@ -182,7 +182,8 @@ export class AuthImpl implements AuthInternal, _FirebaseService { } // Update current Auth state. Either a new login or logout. - await this._updateCurrentUser(user); + // Skip blocking callbacks, they should not apply to a change in another tab. + await this._updateCurrentUser(user, /* skipBeforeStateCallbacks */ true); } private async initializeCurrentUser( @@ -314,7 +315,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService { return this._updateCurrentUser(user && user._clone(this)); } - async _updateCurrentUser(user: User | null): Promise { + async _updateCurrentUser(user: User | null, skipBeforeStateCallbacks: boolean = false): Promise { if (this._deleted) { return; } @@ -325,7 +326,10 @@ export class AuthImpl implements AuthInternal, _FirebaseService { AuthErrorCode.TENANT_ID_MISMATCH ); } - await this._runBeforeStateCallbacks(user); + + if (!skipBeforeStateCallbacks) { + await this._runBeforeStateCallbacks(user); + } return this.queue(async () => { await this.directlySetCurrentUser(user as UserInternal | null); @@ -339,18 +343,22 @@ export class AuthImpl implements AuthInternal, _FirebaseService { await beforeStateCallback(user); } } catch (e) { - throw this._errorFactory.create(AuthErrorCode.LOGIN_BLOCKED, { message: e.message }); + throw this._errorFactory.create( + AuthErrorCode.LOGIN_BLOCKED, { originalMessage: e.message }); } } async signOut(): Promise { + // Run first, to block _setRedirectUser() if any callbacks fail. await this._runBeforeStateCallbacks(null); // Clear the redirect user when signOut is called if (this.redirectPersistenceManager || this._popupRedirectResolver) { await this._setRedirectUser(null); } - return this._updateCurrentUser(null); + // Prevent callbacks from being called again in _updateCurrentUser, as + // they were already called in the first line. + return this._updateCurrentUser(null, /* skipBeforeStateCallbacks */ true); } setPersistence(persistence: Persistence): Promise { diff --git a/packages/auth/src/core/errors.ts b/packages/auth/src/core/errors.ts index 332ca35ccc9..984f4afc509 100644 --- a/packages/auth/src/core/errors.ts +++ b/packages/auth/src/core/errors.ts @@ -246,7 +246,7 @@ function _debugErrorMap(): ErrorMap { 'The verification ID used to create the phone auth credential is invalid.', [AuthErrorCode.INVALID_TENANT_ID]: "The Auth instance's tenant ID is invalid.", - [AuthErrorCode.LOGIN_BLOCKED]: "Login blocked by user-provided method.", + [AuthErrorCode.LOGIN_BLOCKED]: "Login blocked by user-provided method: {$originalMessage}", [AuthErrorCode.MISSING_ANDROID_PACKAGE_NAME]: 'An Android Package Name must be provided if the Android App is required to be installed.', [AuthErrorCode.MISSING_AUTH_DOMAIN]: @@ -430,7 +430,7 @@ export interface AuthErrorParams extends GenericAuthErrorParams { [AuthErrorCode.ARGUMENT_ERROR]: { appName?: AppName }; [AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH]: { appName?: AppName }; [AuthErrorCode.INTERNAL_ERROR]: { appName?: AppName }; - [AuthErrorCode.LOGIN_BLOCKED]: { message?: string }; + [AuthErrorCode.LOGIN_BLOCKED]: { appName?: AppName, originalMessage?: string }; [AuthErrorCode.OPERATION_NOT_SUPPORTED]: { appName?: AppName }; [AuthErrorCode.NO_AUTH_EVENT]: { appName?: AppName }; [AuthErrorCode.MFA_REQUIRED]: { From 06e6a13026b891f66811538fbb78870e4467b674 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Fri, 1 Apr 2022 11:50:49 -0700 Subject: [PATCH 5/6] Address comments and add tests --- common/api-review/auth.api.md | 1 + packages/auth/src/core/auth/auth_impl.test.ts | 76 ++++++++++++++++++- packages/auth/src/core/auth/auth_impl.ts | 8 +- packages/auth/src/model/public_types.ts | 10 +++ 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/common/api-review/auth.api.md b/common/api-review/auth.api.md index 354ab2b2ee8..4dc16f2f04b 100644 --- a/common/api-review/auth.api.md +++ b/common/api-review/auth.api.md @@ -81,6 +81,7 @@ export function applyActionCode(auth: Auth, oobCode: string): Promise; // @public export interface Auth { readonly app: FirebaseApp; + beforeAuthStateChanged(callback: (user: User | null) => void | Promise): Unsubscribe; readonly config: Config; readonly currentUser: User | null; readonly emulatorConfig: EmulatorConfig | null; diff --git a/packages/auth/src/core/auth/auth_impl.test.ts b/packages/auth/src/core/auth/auth_impl.test.ts index 4e226973a0c..bbac17423d3 100644 --- a/packages/auth/src/core/auth/auth_impl.test.ts +++ b/packages/auth/src/core/auth/auth_impl.test.ts @@ -34,6 +34,7 @@ import * as reload from '../user/reload'; import { AuthImpl, DefaultConfig } from './auth_impl'; import { _initializeAuthInstance } from './initialize'; import { ClientPlatform } from '../util/version'; +import { AuthErrorCode } from '../errors'; use(sinonChai); use(chaiAsPromised); @@ -138,6 +139,11 @@ describe('core/auth/auth_impl', () => { expect(persistenceStub._remove).to.have.been.called; expect(auth.currentUser).to.be.null; }); + it('is blocked if a beforeAuthStateChanged callback throws', async () => { + await auth._updateCurrentUser(testUser(auth, 'test')); + auth.beforeAuthStateChanged(sinon.stub().throws()); + await expect(auth.signOut()).to.be.rejectedWith(AuthErrorCode.LOGIN_BLOCKED); + }); }); describe('#useDeviceLanguage', () => { @@ -208,20 +214,24 @@ describe('core/auth/auth_impl', () => { let user: UserInternal; let authStateCallback: sinon.SinonSpy; let idTokenCallback: sinon.SinonSpy; + let beforeAuthCallback: sinon.SinonSpy; beforeEach(() => { user = testUser(auth, 'uid'); authStateCallback = sinon.spy(); idTokenCallback = sinon.spy(); + beforeAuthCallback = sinon.spy(); }); context('initially currentUser is null', () => { beforeEach(async () => { auth.onAuthStateChanged(authStateCallback); auth.onIdTokenChanged(idTokenCallback); + auth.beforeAuthStateChanged(beforeAuthCallback); await auth._updateCurrentUser(null); authStateCallback.resetHistory(); idTokenCallback.resetHistory(); + beforeAuthCallback.resetHistory(); }); it('onAuthStateChange triggers on log in', async () => { @@ -233,15 +243,22 @@ describe('core/auth/auth_impl', () => { await auth._updateCurrentUser(user); expect(idTokenCallback).to.have.been.calledWith(user); }); + + it('beforeAuthStateChanged triggers on log in', async () => { + await auth._updateCurrentUser(user); + expect(beforeAuthCallback).to.have.been.calledWith(user); + }); }); context('initially currentUser is user', () => { beforeEach(async () => { auth.onAuthStateChanged(authStateCallback); auth.onIdTokenChanged(idTokenCallback); + auth.beforeAuthStateChanged(beforeAuthCallback); await auth._updateCurrentUser(user); authStateCallback.resetHistory(); idTokenCallback.resetHistory(); + beforeAuthCallback.resetHistory(); }); it('onAuthStateChange triggers on log out', async () => { @@ -254,6 +271,11 @@ describe('core/auth/auth_impl', () => { expect(idTokenCallback).to.have.been.calledWith(null); }); + it('beforeAuthStateChanged triggers on log out', async () => { + await auth._updateCurrentUser(null); + expect(beforeAuthCallback).to.have.been.calledWith(null); + }); + it('onAuthStateChange does not trigger for user props change', async () => { user.photoURL = 'blah'; await auth._updateCurrentUser(user); @@ -300,21 +322,61 @@ describe('core/auth/auth_impl', () => { expect(cb1).to.have.been.calledWith(user); expect(cb2).to.have.been.calledWith(user); }); + + it('beforeAuthStateChange works for multiple listeners', async () => { + const cb1 = sinon.spy(); + const cb2 = sinon.spy(); + auth.beforeAuthStateChanged(cb1); + auth.beforeAuthStateChanged(cb2); + await auth._updateCurrentUser(null); + cb1.resetHistory(); + cb2.resetHistory(); + + await auth._updateCurrentUser(user); + expect(cb1).to.have.been.calledWith(user); + expect(cb2).to.have.been.calledWith(user); + }); + + it('_updateCurrentUser throws if a beforeAuthStateChange callback throws', async () => { + await auth._updateCurrentUser(null); + const cb1 = sinon.stub().throws(); + const cb2 = sinon.spy(); + auth.beforeAuthStateChanged(cb1); + auth.beforeAuthStateChanged(cb2); + + await expect(auth._updateCurrentUser(user)).to.be.rejectedWith(AuthErrorCode.LOGIN_BLOCKED); + expect(cb2).not.to.be.called; + }); + + it('_updateCurrentUser throws if a beforeAuthStateChange callback rejects', async () => { + await auth._updateCurrentUser(null); + const cb1 = sinon.stub().rejects(); + const cb2 = sinon.spy(); + auth.beforeAuthStateChanged(cb1); + auth.beforeAuthStateChanged(cb2); + + await expect(auth._updateCurrentUser(user)).to.be.rejectedWith(AuthErrorCode.LOGIN_BLOCKED); + expect(cb2).not.to.be.called; + }); }); }); describe('#_onStorageEvent', () => { let authStateCallback: sinon.SinonSpy; let idTokenCallback: sinon.SinonSpy; + let beforeStateCallback: sinon.SinonSpy; beforeEach(async () => { authStateCallback = sinon.spy(); idTokenCallback = sinon.spy(); + beforeStateCallback = sinon.spy(); auth.onAuthStateChanged(authStateCallback); auth.onIdTokenChanged(idTokenCallback); + auth.beforeAuthStateChanged(beforeStateCallback); await auth._updateCurrentUser(null); // force event handlers to clear out authStateCallback.resetHistory(); idTokenCallback.resetHistory(); + beforeStateCallback.resetHistory(); }); context('previously logged out', () => { @@ -324,6 +386,7 @@ describe('core/auth/auth_impl', () => { expect(authStateCallback).not.to.have.been.called; expect(idTokenCallback).not.to.have.been.called; + expect(beforeStateCallback).not.to.have.been.called; }); }); @@ -341,6 +404,8 @@ describe('core/auth/auth_impl', () => { expect(auth.currentUser?.toJSON()).to.eql(user.toJSON()); expect(authStateCallback).to.have.been.called; expect(idTokenCallback).to.have.been.called; + // This should never be called on a storage event. + expect(beforeStateCallback).not.to.have.been.called; }); }); }); @@ -353,6 +418,7 @@ describe('core/auth/auth_impl', () => { await auth._updateCurrentUser(user); authStateCallback.resetHistory(); idTokenCallback.resetHistory(); + beforeStateCallback.resetHistory(); }); context('now logged out', () => { @@ -366,6 +432,8 @@ describe('core/auth/auth_impl', () => { expect(auth.currentUser).to.be.null; expect(authStateCallback).to.have.been.called; expect(idTokenCallback).to.have.been.called; + // This should never be called on a storage event. + expect(beforeStateCallback).not.to.have.been.called; }); }); @@ -378,6 +446,7 @@ describe('core/auth/auth_impl', () => { expect(auth.currentUser?.toJSON()).to.eql(user.toJSON()); expect(authStateCallback).not.to.have.been.called; expect(idTokenCallback).not.to.have.been.called; + expect(beforeStateCallback).not.to.have.been.called; }); it('should update fields if they have changed', async () => { @@ -391,6 +460,7 @@ describe('core/auth/auth_impl', () => { expect(auth.currentUser?.displayName).to.eq('other-name'); expect(authStateCallback).not.to.have.been.called; expect(idTokenCallback).not.to.have.been.called; + expect(beforeStateCallback).not.to.have.been.called; }); it('should update tokens if they have changed', async () => { @@ -407,6 +477,8 @@ describe('core/auth/auth_impl', () => { ).to.eq('new-access-token'); expect(authStateCallback).not.to.have.been.called; expect(idTokenCallback).to.have.been.called; + // This should never be called on a storage event. + expect(beforeStateCallback).not.to.have.been.called; }); }); @@ -420,6 +492,8 @@ describe('core/auth/auth_impl', () => { expect(auth.currentUser?.toJSON()).to.eql(newUser.toJSON()); expect(authStateCallback).to.have.been.called; expect(idTokenCallback).to.have.been.called; + // This should never be called on a storage event. + expect(beforeStateCallback).not.to.have.been.called; }); }); }); @@ -461,7 +535,7 @@ describe('core/auth/auth_impl', () => { }); }); - context ('#_getAdditionalHeaders', () => { + context('#_getAdditionalHeaders', () => { it('always adds the client version', async () => { expect(await auth._getAdditionalHeaders()).to.eql({ 'X-Client-Version': 'v', diff --git a/packages/auth/src/core/auth/auth_impl.ts b/packages/auth/src/core/auth/auth_impl.ts index 263b7f611bf..421852176c9 100644 --- a/packages/auth/src/core/auth/auth_impl.ts +++ b/packages/auth/src/core/auth/auth_impl.ts @@ -225,6 +225,10 @@ export class AuthImpl implements AuthInternal, _FirebaseService { _assert(this._popupRedirectResolver, this, AuthErrorCode.ARGUMENT_ERROR); await this.getOrInitRedirectPersistenceManager(); + // At this point in the flow, this is a redirect user. Run blocking + // middleware callbacks before setting the user. + await this._runBeforeStateCallbacks(storedUser); + // If the redirect user's event ID matches the current user's event ID, // DO NOT reload the current user, otherwise they'll be cleared from storage. // This is important for the reauthenticateWithRedirect() flow. @@ -338,6 +342,9 @@ export class AuthImpl implements AuthInternal, _FirebaseService { } async _runBeforeStateCallbacks(user: User | null): Promise { + if (this.currentUser === user) { + return; + } try { for (const beforeStateCallback of this.beforeStateQueue) { await beforeStateCallback(user); @@ -575,7 +582,6 @@ export class AuthImpl implements AuthInternal, _FirebaseService { * should only be called from within a queued callback. This is necessary * because the queue shouldn't rely on another queued callback. */ - // TODO: Find where this is called and see if we can run the middleware before it private async directlySetCurrentUser( user: UserInternal | null ): Promise { diff --git a/packages/auth/src/model/public_types.ts b/packages/auth/src/model/public_types.ts index 1664d56313d..f92630eedae 100644 --- a/packages/auth/src/model/public_types.ts +++ b/packages/auth/src/model/public_types.ts @@ -254,6 +254,16 @@ export interface Auth { error?: ErrorFn, completed?: CompleteFn ): Unsubscribe; + /** + * Adds a blocking callback that runs before an auth state change + * sets a new user. + * + * @param callback - callback triggered before new user value is set. + * If this throws, it will block the user from being set. + */ + beforeAuthStateChanged( + callback: (user: User | null) => void | Promise + ): Unsubscribe; /** * Adds an observer for changes to the signed-in user's ID token. * From ad7432611413541a47984f466f7cb7dd3c9e45dd Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 4 Apr 2022 13:12:40 -0700 Subject: [PATCH 6/6] Add try/catch --- packages/auth/src/core/auth/auth_impl.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/auth/src/core/auth/auth_impl.ts b/packages/auth/src/core/auth/auth_impl.ts index 421852176c9..37d5ea515dd 100644 --- a/packages/auth/src/core/auth/auth_impl.ts +++ b/packages/auth/src/core/auth/auth_impl.ts @@ -227,7 +227,11 @@ export class AuthImpl implements AuthInternal, _FirebaseService { // At this point in the flow, this is a redirect user. Run blocking // middleware callbacks before setting the user. - await this._runBeforeStateCallbacks(storedUser); + try { + await this._runBeforeStateCallbacks(storedUser); + } catch(e) { + return; + } // If the redirect user's event ID matches the current user's event ID, // DO NOT reload the current user, otherwise they'll be cleared from storage.