diff --git a/packages/app-check/.changeset/chatty-laws-sleep.md b/packages/app-check/.changeset/chatty-laws-sleep.md new file mode 100644 index 00000000000..634ba952c67 --- /dev/null +++ b/packages/app-check/.changeset/chatty-laws-sleep.md @@ -0,0 +1,5 @@ +--- +'@firebase/app-check': patch +--- + +Prevent redundant exchangeToken calls in debug mode diff --git a/packages/app-check/src/internal-api.test.ts b/packages/app-check/src/internal-api.test.ts index 5d6b88f1c32..ce89430a15b 100644 --- a/packages/app-check/src/internal-api.test.ts +++ b/packages/app-check/src/internal-api.test.ts @@ -630,6 +630,31 @@ describe('internal api', () => { expect(token).to.deep.equal({ token: fakeRecaptchaAppCheckToken.token }); }); + it('exchanges debug token only once if debug mode with no cached token', async () => { + const exchangeTokenStub: SinonStub = stub( + client, + 'exchangeToken' + ).returns(Promise.resolve(fakeRecaptchaAppCheckToken)); + const debugState = getDebugState(); + debugState.enabled = true; + debugState.token = new Deferred(); + debugState.token.resolve('my-debug-token'); + const appCheck = initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) + }); + const appCheckService = appCheck as AppCheckService; + const [token1, token2] = await Promise.all([ + getToken(appCheckService), + getToken(appCheckService) + ]); + expect(exchangeTokenStub.args[0][0].body['debug_token']).to.equal( + 'my-debug-token' + ); + expect(token1).to.deep.equal({ token: fakeRecaptchaAppCheckToken.token }); + expect(token2).to.deep.equal({ token: fakeRecaptchaAppCheckToken.token }); + expect(exchangeTokenStub).to.be.calledOnce; + }); + it('throttles for a period less than 1d on 503', async () => { // More detailed check of exponential backoff in providers.test.ts const appCheck = initializeAppCheck(app, { diff --git a/packages/app-check/src/internal-api.ts b/packages/app-check/src/internal-api.ts index eddf043c843..158c723a057 100644 --- a/packages/app-check/src/internal-api.ts +++ b/packages/app-check/src/internal-api.ts @@ -118,10 +118,11 @@ export async function getToken( */ if (isDebugMode()) { try { + const debugToken = await getDebugToken(); // Avoid making another call to the exchange endpoint if one is in flight. if (!state.exchangeTokenPromise) { state.exchangeTokenPromise = exchangeToken( - getExchangeDebugTokenRequest(app, await getDebugToken()), + getExchangeDebugTokenRequest(app, debugToken), appCheck.heartbeatServiceProvider ).finally(() => { // Clear promise when settled - either resolved or rejected.