Skip to content

Commit f0f0f84

Browse files
committed
clean up refresher start logic
1 parent 1d86d21 commit f0f0f84

File tree

2 files changed

+40
-25
lines changed

2 files changed

+40
-25
lines changed

packages/app-check/src/internal-api.test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ describe('internal api', () => {
404404
expect(getState(app).tokenObservers[0].next).to.equal(listener);
405405
});
406406

407-
it('starts proactively refreshing token after adding the first listener', () => {
407+
it('starts proactively refreshing token after adding the first listener', async () => {
408408
const listener = (): void => {};
409409
setState(app, {
410410
...getState(app),
@@ -420,6 +420,10 @@ describe('internal api', () => {
420420
listener
421421
);
422422

423+
// addTokenListener() waits for the result of cachedTokenPromise
424+
// before starting the refresher
425+
await getState(app).cachedTokenPromise;
426+
423427
expect(getState(app).tokenRefresher?.isRunning()).to.be.true;
424428
});
425429

@@ -430,6 +434,7 @@ describe('internal api', () => {
430434

431435
setState(app, {
432436
...getState(app),
437+
cachedTokenPromise: Promise.resolve(undefined),
433438
token: {
434439
token: `fake-memory-app-check-token`,
435440
expireTimeMillis: Date.now() + 60000,
@@ -493,7 +498,7 @@ describe('internal api', () => {
493498
expect(getState(app).tokenObservers.length).to.equal(0);
494499
});
495500

496-
it('should stop proactively refreshing token after deleting the last listener', () => {
501+
it('should stop proactively refreshing token after deleting the last listener', async () => {
497502
const listener = (): void => {};
498503
setState(app, { ...getState(app), isTokenAutoRefreshEnabled: true });
499504
setState(app, {
@@ -506,6 +511,11 @@ describe('internal api', () => {
506511
ListenerType.INTERNAL,
507512
listener
508513
);
514+
515+
// addTokenListener() waits for the result of cachedTokenPromise
516+
// before starting the refresher
517+
await getState(app).cachedTokenPromise;
518+
509519
expect(getState(app).tokenObservers.length).to.equal(1);
510520
expect(getState(app).tokenRefresher?.isRunning()).to.be.true;
511521

packages/app-check/src/internal-api.ts

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export async function getToken(
165165
await writeTokenToStorage(app, token);
166166
}
167167

168-
if (!shouldCallListeners) {
168+
if (shouldCallListeners) {
169169
notifyTokenListeners(app, interopTokenResult);
170170
}
171171
return interopTokenResult;
@@ -184,17 +184,20 @@ export function addTokenListener(
184184
error: onError,
185185
type
186186
};
187-
const newState = {
187+
setState(app, {
188188
...state,
189189
tokenObservers: [...state.tokenObservers, tokenObserver]
190-
};
190+
});
191191

192192
// Invoke the listener async immediately if there is a valid token
193193
// in memory.
194194
if (state.token && isValid(state.token)) {
195195
const validToken = state.token;
196196
Promise.resolve()
197-
.then(() => listener({ token: validToken.token }))
197+
.then(() => {
198+
listener({ token: validToken.token });
199+
initTokenRefresher(appCheck);
200+
})
198201
.catch(() => {
199202
/* we don't care about exceptions thrown in listeners */
200203
});
@@ -206,28 +209,12 @@ export function addTokenListener(
206209
* in state and calls the exchange endpoint if not. We should first let the
207210
* IndexedDB check have a chance to populate state if it can.
208211
*
209-
* We want to call the listener if the cached token check returns something
210-
* but cachedTokenPromise handler already will notify all listeners on the
211-
* first fetch, and we don't want duplicate calls to the listener.
212+
* Listener call isn't needed here because cachedTokenPromise will call any
213+
* listeners that exist when it resolves.
212214
*/
213215

214216
// state.cachedTokenPromise is always populated in `activate()`.
215-
void state.cachedTokenPromise!.then(() => {
216-
if (!newState.tokenRefresher) {
217-
const tokenRefresher = createTokenRefresher(appCheck);
218-
newState.tokenRefresher = tokenRefresher;
219-
}
220-
// Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
221-
// is not true.
222-
if (
223-
!newState.tokenRefresher.isRunning() &&
224-
state.isTokenAutoRefreshEnabled
225-
) {
226-
newState.tokenRefresher.start();
227-
}
228-
});
229-
230-
setState(app, newState);
217+
void state.cachedTokenPromise!.then(() => initTokenRefresher(appCheck));
231218
}
232219

233220
export function removeTokenListener(
@@ -253,6 +240,24 @@ export function removeTokenListener(
253240
});
254241
}
255242

243+
/**
244+
* Logic to create and start refresher as needed.
245+
*/
246+
function initTokenRefresher(appCheck: AppCheckService): void {
247+
const { app } = appCheck;
248+
const state = getState(app);
249+
// Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
250+
// is not true.
251+
let refresher: Refresher | undefined = state.tokenRefresher;
252+
if (!refresher) {
253+
refresher = createTokenRefresher(appCheck);
254+
setState(app, { ...state, tokenRefresher: refresher });
255+
}
256+
if (!refresher.isRunning() && state.isTokenAutoRefreshEnabled) {
257+
refresher.start();
258+
}
259+
}
260+
256261
function createTokenRefresher(appCheck: AppCheckService): Refresher {
257262
const { app } = appCheck;
258263
return new Refresher(

0 commit comments

Comments
 (0)