Skip to content

Commit 9e92e91

Browse files
authored
Handle app deletion a little more gracefully in auth-exp and auth-compat-exp (#3917)
* Handle app deletion a little more gracefully in auth-exp and auth-compat-exp * Formatting * Add some tests * Formatting
1 parent df27732 commit 9e92e91

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

packages-exp/auth-compat-exp/src/auth.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import { FirebaseApp } from '@firebase/app-types';
19+
import { _FirebaseService } from '@firebase/app-types-exp';
1920
import * as impl from '@firebase/auth-exp/internal';
2021
import * as compat from '@firebase/auth-types';
2122
import * as externs from '@firebase/auth-types-exp';
@@ -35,11 +36,15 @@ import {
3536
} from './user_credential';
3637
import { unwrap, Wrapper } from './wrap';
3738

38-
export class Auth implements compat.FirebaseAuth, Wrapper<externs.Auth> {
39+
export class Auth
40+
implements compat.FirebaseAuth, Wrapper<externs.Auth>, _FirebaseService {
3941
// private readonly auth: impl.AuthImpl;
4042

4143
constructor(readonly app: FirebaseApp, private readonly auth: impl.AuthImpl) {
4244
const { apiKey } = app.options;
45+
if (this.auth._deleted) {
46+
return;
47+
}
4348

4449
// TODO(avolkovi): Implement proper persistence fallback
4550
const hierarchy = [impl.indexedDBLocalPersistence].map<impl.Persistence>(
@@ -289,6 +294,9 @@ export class Auth implements compat.FirebaseAuth, Wrapper<externs.Auth> {
289294
unwrap(): externs.Auth {
290295
return this.auth;
291296
}
297+
_delete(): Promise<void> {
298+
return this.auth._delete();
299+
}
292300
}
293301

294302
function wrapObservers(

packages-exp/auth-exp/src/core/auth/auth_impl.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { Persistence } from '../persistence';
3333
import { inMemoryPersistence } from '../persistence/in_memory';
3434
import { _getInstance } from '../util/instantiator';
3535
import * as navigator from '../util/navigator';
36+
import * as reload from '../user/reload';
3637
import {
3738
_castAuth,
3839
AuthImpl,
@@ -410,6 +411,41 @@ describe('core/auth/auth_impl', () => {
410411
});
411412
});
412413
});
414+
415+
context('#_delete', () => {
416+
beforeEach(async () => {
417+
sinon.stub(reload, '_reloadWithoutSaving').returns(Promise.resolve());
418+
});
419+
420+
it('prevents initialization from completing', async () => {
421+
const authImpl = new AuthImpl(FAKE_APP, {
422+
apiKey: FAKE_APP.options.apiKey!,
423+
apiHost: DEFAULT_API_HOST,
424+
apiScheme: DEFAULT_API_SCHEME,
425+
tokenApiHost: DEFAULT_TOKEN_API_HOST,
426+
sdkClientVersion: 'v'
427+
});
428+
429+
persistenceStub._get.returns(
430+
Promise.resolve(testUser(auth, 'uid').toJSON())
431+
);
432+
await authImpl._delete();
433+
await authImpl._initializeWithPersistence([
434+
persistenceStub as Persistence
435+
]);
436+
expect(authImpl.currentUser).to.be.null;
437+
});
438+
439+
it('no longer calls listeners', async () => {
440+
const spy = sinon.spy();
441+
auth.onAuthStateChanged(spy);
442+
await Promise.resolve();
443+
spy.resetHistory();
444+
await (auth as AuthImpl)._delete();
445+
await auth.updateCurrentUser(testUser(auth, 'blah'));
446+
expect(spy).not.to.have.been.called;
447+
});
448+
});
413449
});
414450

415451
// These tests are separate because they are using a different auth with

packages-exp/auth-exp/src/core/auth/auth_impl.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export class AuthImpl implements Auth, _FirebaseService {
6363
// initialization
6464
_canInitEmulator = true;
6565
_isInitialized = false;
66+
_deleted = false;
6667
_initializationPromise: Promise<void> | null = null;
6768
_popupRedirectResolver: PopupRedirectResolver | null = null;
6869
readonly name: string;
@@ -86,7 +87,13 @@ export class AuthImpl implements Auth, _FirebaseService {
8687
persistenceHierarchy: Persistence[],
8788
popupRedirectResolver?: externs.PopupRedirectResolver
8889
): Promise<void> {
90+
// Have to check for app deletion throughout initialization (after each
91+
// promise resolution)
8992
this._initializationPromise = this.queue(async () => {
93+
if (this._deleted) {
94+
return;
95+
}
96+
9097
if (popupRedirectResolver) {
9198
this._popupRedirectResolver = _getInstance(popupRedirectResolver);
9299
}
@@ -96,8 +103,16 @@ export class AuthImpl implements Auth, _FirebaseService {
96103
persistenceHierarchy
97104
);
98105

106+
if (this._deleted) {
107+
return;
108+
}
109+
99110
await this.initializeCurrentUser();
100111

112+
if (this._deleted) {
113+
return;
114+
}
115+
101116
this._isInitialized = true;
102117
this.notifyAuthListeners();
103118
});
@@ -109,6 +124,10 @@ export class AuthImpl implements Auth, _FirebaseService {
109124
* If the persistence is changed in another window, the user manager will let us know
110125
*/
111126
async _onStorageEvent(): Promise<void> {
127+
if (this._deleted) {
128+
return;
129+
}
130+
112131
const user = await this.assertedPersistence.getCurrentUser();
113132

114133
if (!this.currentUser && !user) {
@@ -189,10 +208,13 @@ export class AuthImpl implements Auth, _FirebaseService {
189208
}
190209

191210
async _delete(): Promise<void> {
192-
// TODO: Determine what we want to do in this case
211+
this._deleted = true;
193212
}
194213

195214
async updateCurrentUser(user: externs.User | null): Promise<void> {
215+
if (this._deleted) {
216+
return;
217+
}
196218
if (user) {
197219
assert(
198220
this.tenantId === user.tenantId,
@@ -357,6 +379,10 @@ export class AuthImpl implements Auth, _FirebaseService {
357379
error?: ErrorFn,
358380
completed?: CompleteFn
359381
): Unsubscribe {
382+
if (this._deleted) {
383+
return () => {};
384+
}
385+
360386
const cb =
361387
typeof nextOrObserver === 'function'
362388
? nextOrObserver

0 commit comments

Comments
 (0)