Skip to content

Commit 3da2039

Browse files
authored
Throw an error when the Auth (exp) internal instance is used by another SDK before Auth initialization (#4428)
* Make the prod error map be verbose for this one error * Formatting
1 parent e8811c0 commit 3da2039

File tree

3 files changed

+74
-2
lines changed

3 files changed

+74
-2
lines changed

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

+45-1
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { expect } from 'chai';
18+
import { FirebaseError } from '@firebase/util';
19+
import { expect, use } from 'chai';
1920
import * as sinon from 'sinon';
21+
import * as chaiAsPromised from 'chai-as-promised';
2022

2123
import { testAuth, testUser } from '../../../test/helpers/mock_auth';
2224
import { Auth } from '../../model/auth';
2325
import { User } from '../../model/user';
2426
import { AuthInternal } from './firebase_internal';
2527

28+
use(chaiAsPromised);
29+
2630
describe('core/auth/firebase_internal', () => {
2731
let auth: Auth;
2832
let authInternal: AuthInternal;
@@ -45,6 +49,16 @@ describe('core/auth/firebase_internal', () => {
4549
await auth._updateCurrentUser(user);
4650
expect(authInternal.getUid()).to.eq('uid');
4751
});
52+
53+
it('errors if Auth is not initialized', () => {
54+
delete ((auth as unknown) as Record<string, unknown>)[
55+
'_initializationPromise'
56+
];
57+
expect(() => authInternal.getUid()).to.throw(
58+
FirebaseError,
59+
'auth/dependent-sdk-initialized-before-auth'
60+
);
61+
});
4862
});
4963

5064
context('getToken', () => {
@@ -62,6 +76,16 @@ describe('core/auth/firebase_internal', () => {
6276
accessToken: 'access-token'
6377
});
6478
});
79+
80+
it('errors if Auth is not initialized', async () => {
81+
delete ((auth as unknown) as Record<string, unknown>)[
82+
'_initializationPromise'
83+
];
84+
await expect(authInternal.getToken()).to.be.rejectedWith(
85+
FirebaseError,
86+
'auth/dependent-sdk-initialized-before-auth'
87+
);
88+
});
6589
});
6690

6791
context('token listeners', () => {
@@ -115,6 +139,16 @@ describe('core/auth/firebase_internal', () => {
115139

116140
expect(tokenCount).to.eq(5);
117141
});
142+
143+
it('errors if Auth is not initialized', () => {
144+
delete ((auth as unknown) as Record<string, unknown>)[
145+
'_initializationPromise'
146+
];
147+
expect(() => authInternal.addAuthTokenListener(() => {})).to.throw(
148+
FirebaseError,
149+
'auth/dependent-sdk-initialized-before-auth'
150+
);
151+
});
118152
});
119153

120154
context('removeAuthTokenListener', () => {
@@ -168,6 +202,16 @@ describe('core/auth/firebase_internal', () => {
168202
authInternal.addAuthTokenListener(listenerB);
169203
expect(isProactiveRefresh).to.be.true;
170204
});
205+
206+
it('errors if Auth is not initialized', () => {
207+
delete ((auth as unknown) as Record<string, unknown>)[
208+
'_initializationPromise'
209+
];
210+
expect(() => authInternal.removeAuthTokenListener(() => {})).to.throw(
211+
FirebaseError,
212+
'auth/dependent-sdk-initialized-before-auth'
213+
);
214+
});
171215
});
172216
});
173217
});

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

+13
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { FirebaseAuthInternal } from '@firebase/auth-interop-types';
2020

2121
import { Auth } from '../../model/auth';
2222
import { User } from '../../model/user';
23+
import { _assert } from '../util/assert';
24+
import { AuthErrorCode } from '../errors';
2325

2426
interface TokenListener {
2527
(tok: string | null): unknown;
@@ -34,12 +36,14 @@ export class AuthInternal implements FirebaseAuthInternal {
3436
constructor(private readonly auth: Auth) {}
3537

3638
getUid(): string | null {
39+
this.assertAuthConfigured();
3740
return this.auth.currentUser?.uid || null;
3841
}
3942

4043
async getToken(
4144
forceRefresh?: boolean
4245
): Promise<{ accessToken: string } | null> {
46+
this.assertAuthConfigured();
4347
await this.auth._initializationPromise;
4448
if (!this.auth.currentUser) {
4549
return null;
@@ -50,6 +54,7 @@ export class AuthInternal implements FirebaseAuthInternal {
5054
}
5155

5256
addAuthTokenListener(listener: TokenListener): void {
57+
this.assertAuthConfigured();
5358
if (this.internalListeners.has(listener)) {
5459
return;
5560
}
@@ -62,6 +67,7 @@ export class AuthInternal implements FirebaseAuthInternal {
6267
}
6368

6469
removeAuthTokenListener(listener: TokenListener): void {
70+
this.assertAuthConfigured();
6571
const unsubscribe = this.internalListeners.get(listener);
6672
if (!unsubscribe) {
6773
return;
@@ -72,6 +78,13 @@ export class AuthInternal implements FirebaseAuthInternal {
7278
this.updateProactiveRefresh();
7379
}
7480

81+
private assertAuthConfigured(): void {
82+
_assert(
83+
this.auth._initializationPromise,
84+
AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH
85+
);
86+
}
87+
7588
private updateProactiveRefresh(): void {
7689
if (this.internalListeners.size > 0) {
7790
this.auth._startProactiveRefresh();

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

+16-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const enum AuthErrorCode {
3939
CREDENTIAL_ALREADY_IN_USE = 'credential-already-in-use',
4040
CREDENTIAL_MISMATCH = 'custom-token-mismatch',
4141
CREDENTIAL_TOO_OLD_LOGIN_AGAIN = 'requires-recent-login',
42+
DEPENDENT_SDK_INIT_BEFORE_AUTH = 'dependent-sdk-initialized-before-auth',
4243
DYNAMIC_LINK_NOT_ACTIVATED = 'dynamic-link-not-activated',
4344
EMAIL_CHANGE_NEEDS_VERIFICATION = 'email-change-needs-verification',
4445
EMAIL_EXISTS = 'email-already-in-use',
@@ -152,6 +153,10 @@ function _debugErrorMap(): ErrorMap<AuthErrorCode> {
152153
[AuthErrorCode.CREDENTIAL_TOO_OLD_LOGIN_AGAIN]:
153154
'This operation is sensitive and requires recent authentication. Log in ' +
154155
'again before retrying this request.',
156+
[AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH]:
157+
'Another Firebase SDK was initialized and is trying to use Auth before Auth is ' +
158+
'initialized. Please be sure to call `initializeAuth` or `getAuth` before ' +
159+
'starting any other Firebase SDK.',
155160
[AuthErrorCode.DYNAMIC_LINK_NOT_ACTIVATED]:
156161
'Please activate Dynamic Links in the Firebase Console and agree to the terms and ' +
157162
'conditions.',
@@ -351,7 +356,15 @@ export interface ErrorMapRetriever extends externs.AuthErrorMap {
351356
}
352357

353358
function _prodErrorMap(): ErrorMap<AuthErrorCode> {
354-
return {} as ErrorMap<AuthErrorCode>;
359+
// We will include this one message in the prod error map since by the very
360+
// nature of this error, developers will never be able to see the message
361+
// using the debugErrorMap (which is installed during auth initialization).
362+
return {
363+
[AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH]:
364+
'Another Firebase SDK was initialized and is trying to use Auth before Auth is ' +
365+
'initialized. Please be sure to call `initializeAuth` or `getAuth` before ' +
366+
'starting any other Firebase SDK.'
367+
} as ErrorMap<AuthErrorCode>;
355368
}
356369

357370
/**
@@ -386,6 +399,7 @@ type GenericAuthErrorParams = {
386399
[key in Exclude<
387400
AuthErrorCode,
388401
| AuthErrorCode.ARGUMENT_ERROR
402+
| AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH
389403
| AuthErrorCode.INTERNAL_ERROR
390404
| AuthErrorCode.MFA_REQUIRED
391405
>]: {
@@ -397,6 +411,7 @@ type GenericAuthErrorParams = {
397411

398412
export interface AuthErrorParams extends GenericAuthErrorParams {
399413
[AuthErrorCode.ARGUMENT_ERROR]: { appName?: AppName };
414+
[AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH]: { appName?: AppName };
400415
[AuthErrorCode.INTERNAL_ERROR]: { appName?: AppName };
401416
[AuthErrorCode.MFA_REQUIRED]: {
402417
appName: AppName;

0 commit comments

Comments
 (0)