Skip to content

Commit bb6f337

Browse files
committed
clean up refresher start logic
1 parent ab90e7f commit bb6f337

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
@@ -381,7 +381,7 @@ describe('internal api', () => {
381381
expect(getState(app).tokenObservers[0].next).to.equal(listener);
382382
});
383383

384-
it('starts proactively refreshing token after adding the first listener', () => {
384+
it('starts proactively refreshing token after adding the first listener', async () => {
385385
const listener = (): void => {};
386386
setState(app, {
387387
...getState(app),
@@ -397,6 +397,10 @@ describe('internal api', () => {
397397
listener
398398
);
399399

400+
// addTokenListener() waits for the result of cachedTokenPromise
401+
// before starting the refresher
402+
await getState(app).cachedTokenPromise;
403+
400404
expect(getState(app).tokenRefresher?.isRunning()).to.be.true;
401405
});
402406

@@ -407,6 +411,7 @@ describe('internal api', () => {
407411

408412
setState(app, {
409413
...getState(app),
414+
cachedTokenPromise: Promise.resolve(undefined),
410415
token: {
411416
token: `fake-memory-app-check-token`,
412417
expireTimeMillis: Date.now() + 60000,
@@ -470,7 +475,7 @@ describe('internal api', () => {
470475
expect(getState(app).tokenObservers.length).to.equal(0);
471476
});
472477

473-
it('should stop proactively refreshing token after deleting the last listener', () => {
478+
it('should stop proactively refreshing token after deleting the last listener', async () => {
474479
const listener = (): void => {};
475480
setState(app, { ...getState(app), isTokenAutoRefreshEnabled: true });
476481
setState(app, {
@@ -483,6 +488,11 @@ describe('internal api', () => {
483488
ListenerType.INTERNAL,
484489
listener
485490
);
491+
492+
// addTokenListener() waits for the result of cachedTokenPromise
493+
// before starting the refresher
494+
await getState(app).cachedTokenPromise;
495+
486496
expect(getState(app).tokenObservers.length).to.equal(1);
487497
expect(getState(app).tokenRefresher?.isRunning()).to.be.true;
488498

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)