Skip to content

Commit d3fa466

Browse files
committed
Fix for the auth race condition
1 parent 5a913c9 commit d3fa466

File tree

5 files changed

+28
-15
lines changed

5 files changed

+28
-15
lines changed

packages/auth/src/api/authentication/token.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export async function requestStsToken(
7474
'refresh_token': refreshToken
7575
}).slice(1);
7676
const { tokenApiHost, apiKey } = auth.config;
77-
const url = _getFinalTarget(
77+
const url = await _getFinalTarget(
7878
auth,
7979
tokenApiHost,
8080
Endpoint.TOKEN,

packages/auth/src/api/index.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -509,17 +509,17 @@ describe('api/_performApiRequest', () => {
509509
});
510510

511511
context('_getFinalTarget', () => {
512-
it('works properly with a non-emulated environment', () => {
513-
expect(_getFinalTarget(auth, 'host', '/path', 'query=test')).to.eq(
512+
it('works properly with a non-emulated environment', async () => {
513+
expect(await _getFinalTarget(auth, 'host', '/path', 'query=test')).to.eq(
514514
'mock://host/path?query=test'
515515
);
516516
});
517517

518-
it('works properly with an emulated environment', () => {
518+
it('works properly with an emulated environment', async () => {
519519
(auth.config as ConfigInternal).emulator = {
520520
url: 'http://localhost:5000/'
521521
};
522-
expect(_getFinalTarget(auth, 'host', '/path', 'query=test')).to.eq(
522+
expect(await _getFinalTarget(auth, 'host', '/path', 'query=test')).to.eq(
523523
'http://localhost:5000/host/path?query=test'
524524
);
525525
});

packages/auth/src/api/index.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ export async function _performApiRequest<T, V>(
178178
}
179179

180180
return FetchProvider.fetch()(
181-
_getFinalTarget(auth, auth.config.apiHost, path, query),
181+
await _getFinalTarget(auth, auth.config.apiHost, path, query),
182182
fetchArgs
183183
);
184184
});
@@ -268,12 +268,12 @@ export async function _performSignInRequest<T, V extends IdTokenResponse>(
268268
return serverResponse as V;
269269
}
270270

271-
export function _getFinalTarget(
271+
export async function _getFinalTarget(
272272
auth: Auth,
273273
host: string,
274274
path: string,
275275
query: string
276-
): string {
276+
): Promise<string> {
277277
const base = `${host}${path}?${query}`;
278278

279279
const authInternal = auth as AuthInternal;
@@ -284,13 +284,15 @@ export function _getFinalTarget(
284284
// Cookie auth works by MiTMing the signIn and token endpoints from the developer's backend,
285285
// saving the idToken and refreshToken into cookies, and then redacting the refreshToken
286286
// from the response
287-
if (
288-
authInternal._getPersistenceType() === PersistenceType.COOKIE &&
289-
CookieAuthProxiedEndpoints.includes(path)
290-
) {
291-
const cookiePersistence =
292-
authInternal._getPersistence() as CookiePersistence;
293-
return cookiePersistence._getFinalTarget(finalTarget).toString();
287+
if (CookieAuthProxiedEndpoints.includes(path)) {
288+
// Persistence manager is async, we need to await it. We can't just wait for auth initialized
289+
// here since auth initialization calls this function.
290+
await authInternal._persistenceManagerAvailable;
291+
if (authInternal._getPersistenceType() === PersistenceType.COOKIE) {
292+
const cookiePersistence =
293+
authInternal._getPersistence() as CookiePersistence;
294+
return cookiePersistence._getFinalTarget(finalTarget).toString();
295+
}
294296
}
295297

296298
return finalTarget;

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
120120
_tenantRecaptchaConfigs: Record<string, RecaptchaConfig> = {};
121121
_projectPasswordPolicy: PasswordPolicyInternal | null = null;
122122
_tenantPasswordPolicies: Record<string, PasswordPolicyInternal> = {};
123+
_resolvePersistenceManagerAvailable:
124+
| ((value: void | PromiseLike<void>) => void)
125+
| undefined = undefined;
126+
_persistenceManagerAvailable: Promise<void>;
123127
readonly name: string;
124128

125129
// Tracks the last notified UID for state change listeners to prevent
@@ -139,6 +143,11 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
139143
) {
140144
this.name = app.name;
141145
this.clientVersion = config.sdkClientVersion;
146+
// TODO(jamesdaniels) explore less hacky way to do this, cookie authentication needs
147+
// persistenceMananger to be available. see _getFinalTarget for more context
148+
this._persistenceManagerAvailable = new Promise<void>(
149+
resolve => (this._resolvePersistenceManagerAvailable = resolve)
150+
);
142151
}
143152

144153
_initializeWithPersistence(
@@ -160,6 +169,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
160169
this,
161170
persistenceHierarchy
162171
);
172+
this._resolvePersistenceManagerAvailable?.();
163173

164174
if (this._deleted) {
165175
return;

packages/auth/src/model/auth.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export interface AuthInternal extends Auth {
7272
_canInitEmulator: boolean;
7373
_isInitialized: boolean;
7474
_initializationPromise: Promise<void> | null;
75+
_persistenceManagerAvailable: Promise<void>;
7576
_updateCurrentUser(user: UserInternal | null): Promise<void>;
7677

7778
_onStorageEvent(): void;

0 commit comments

Comments
 (0)