Skip to content

Commit d881748

Browse files
committed
Add isTokenAutoRefreshEnabled setting
1 parent 919413c commit d881748

File tree

8 files changed

+80
-11
lines changed

8 files changed

+80
-11
lines changed

packages/app-check-types/index.d.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@
1818
export interface FirebaseAppCheck {
1919
/**
2020
* Activate AppCheck
21-
* @param siteKeyOrOrovider - reCAPTCHA sitekey or custom token provider
21+
* @param siteKeyOrProvider - reCAPTCHA sitekey or custom token provider
22+
* @param isTokenAutoRefreshEnabled - If true, enables SDK to automatically
23+
* refresh AppCheck token as needed. If undefined, the value will default
24+
* to the value of `app.automaticDataCollectionEnabled`. That property
25+
* defaults to false and can be set in the app config.
2226
*/
23-
activate(siteKeyOrProvider: string | AppCheckProvider): void;
27+
activate(
28+
siteKeyOrProvider: string | AppCheckProvider,
29+
isTokenAutoRefreshEnabled?: boolean
30+
): void;
31+
setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled: boolean): void;
2432
}
2533

2634
interface AppCheckProvider {

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import '../test/setup';
1818
import { expect } from 'chai';
1919
import { stub } from 'sinon';
20-
import { activate } from './api';
20+
import { activate, setTokenAutoRefreshEnabled } from './api';
2121
import {
2222
FAKE_SITE_KEY,
2323
getFakeApp,
@@ -41,6 +41,18 @@ describe('api', () => {
4141
expect(getState(app).activated).to.equal(true);
4242
});
4343

44+
it('isTokenAutoRefreshEnabled value defaults to global setting', () => {
45+
app = getFakeApp({ automaticDataCollectionEnabled: false });
46+
activate(app, FAKE_SITE_KEY);
47+
expect(getState(app).isTokenAutoRefreshEnabled).to.equal(false);
48+
});
49+
50+
it('sets isTokenAutoRefreshEnabled correctly, overriding global setting', () => {
51+
app = getFakeApp({ automaticDataCollectionEnabled: false });
52+
activate(app, FAKE_SITE_KEY, true);
53+
expect(getState(app).isTokenAutoRefreshEnabled).to.equal(true);
54+
});
55+
4456
it('can only be called once', () => {
4557
activate(app, FAKE_SITE_KEY);
4658
expect(() => activate(app, FAKE_SITE_KEY)).to.throw(
@@ -67,4 +79,11 @@ describe('api', () => {
6779
expect(initReCAPTCHAStub).to.have.not.been.called;
6880
});
6981
});
82+
describe('setTokenAutoRefreshEnabled()', () => {
83+
it('sets isTokenAutoRefreshEnabled correctly', () => {
84+
const app = getFakeApp({ automaticDataCollectionEnabled: false });
85+
setTokenAutoRefreshEnabled(app, true);
86+
expect(getState(app).isTokenAutoRefreshEnabled).to.equal(true);
87+
});
88+
});
7089
});

packages/app-check/src/api.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ import { getState, setState, AppCheckState } from './state';
2424
/**
2525
*
2626
* @param app
27-
* @param provider - optional custom attestation provider
27+
* @param siteKeyOrProvider - optional custom attestation provider
28+
* or reCAPTCHA siteKey
29+
* @param isTokenAutoRefreshEnabled - if true, enables auto refresh
30+
* of appCheck token.
2831
*/
2932
export function activate(
3033
app: FirebaseApp,
31-
siteKeyOrProvider: string | AppCheckProvider
34+
siteKeyOrProvider: string | AppCheckProvider,
35+
isTokenAutoRefreshEnabled?: boolean
3236
): void {
3337
const state = getState(app);
3438
if (state.activated) {
@@ -44,6 +48,14 @@ export function activate(
4448
newState.customProvider = siteKeyOrProvider;
4549
}
4650

51+
// Use value of global `automaticDataCollectionEnabled` (which
52+
// itself defaults to false if not specified in config) if
53+
// `isTokenAutoRefreshEnabled` param was not provided by user.
54+
newState.isTokenAutoRefreshEnabled =
55+
isTokenAutoRefreshEnabled === undefined
56+
? app.automaticDataCollectionEnabled
57+
: isTokenAutoRefreshEnabled;
58+
4759
setState(app, newState);
4860

4961
// initialize reCAPTCHA if siteKey is provided
@@ -53,3 +65,20 @@ export function activate(
5365
});
5466
}
5567
}
68+
69+
export function setTokenAutoRefreshEnabled(
70+
app: FirebaseApp,
71+
isTokenAutoRefreshEnabled: boolean
72+
): void {
73+
const state = getState(app);
74+
// This will exist if any product libraries have called
75+
// `addTokenListener()`
76+
if (state.tokenRefresher) {
77+
if (isTokenAutoRefreshEnabled === true) {
78+
state.tokenRefresher.start();
79+
} else {
80+
state.tokenRefresher.stop();
81+
}
82+
}
83+
setState(app, { ...state, isTokenAutoRefreshEnabled });
84+
}

packages/app-check/src/factory.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
import { FirebaseAppCheck, AppCheckProvider } from '@firebase/app-check-types';
19-
import { activate } from './api';
19+
import { activate, setTokenAutoRefreshEnabled } from './api';
2020
import { FirebaseApp } from '@firebase/app-types';
2121
import { FirebaseAppCheckInternal } from '@firebase/app-check-interop-types';
2222
import {
@@ -27,8 +27,12 @@ import {
2727

2828
export function factory(app: FirebaseApp): FirebaseAppCheck {
2929
return {
30-
activate: (siteKeyOrProvider: string | AppCheckProvider) =>
31-
activate(app, siteKeyOrProvider)
30+
activate: (
31+
siteKeyOrProvider: string | AppCheckProvider,
32+
isTokenAutoRefreshEnabled?: boolean
33+
) => activate(app, siteKeyOrProvider, isTokenAutoRefreshEnabled),
34+
setTokenAutoRefreshEnabled: (isTokenAutoRefreshEnabled: boolean) =>
35+
setTokenAutoRefreshEnabled(app, isTokenAutoRefreshEnabled)
3236
};
3337
}
3438

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ describe('internal api', () => {
290290

291291
it('starts proactively refreshing token after adding the first listener', () => {
292292
const listener = (): void => {};
293+
setState(app, { ...getState(app), isTokenAutoRefreshEnabled: true });
293294
expect(getState(app).tokenListeners.length).to.equal(0);
294295
expect(getState(app).tokenRefresher).to.equal(undefined);
295296

@@ -384,6 +385,7 @@ describe('internal api', () => {
384385

385386
it('should stop proactively refreshing token after deleting the last listener', () => {
386387
const listener = (): void => {};
388+
setState(app, { ...getState(app), isTokenAutoRefreshEnabled: true });
387389

388390
addTokenListener(app, listener);
389391
expect(getState(app).tokenListeners.length).to.equal(1);

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,12 @@ export function addTokenListener(
178178
newState.tokenRefresher = tokenRefresher;
179179
}
180180

181-
if (!newState.tokenRefresher.isRunning()) {
181+
// Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
182+
// is not true.
183+
if (
184+
!newState.tokenRefresher.isRunning() &&
185+
state.isTokenAutoRefreshEnabled === true
186+
) {
182187
newState.tokenRefresher.start();
183188
}
184189

packages/app-check/src/state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface AppCheckState {
2929
token?: AppCheckToken;
3030
tokenRefresher?: Refresher;
3131
reCAPTCHAState?: ReCAPTCHAState;
32+
isTokenAutoRefreshEnabled?: boolean;
3233
}
3334

3435
export interface ReCAPTCHAState {

packages/app-check/test/util.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { GreCAPTCHA, RECAPTCHA_URL } from '../src/recaptcha';
2121

2222
export const FAKE_SITE_KEY = 'fake-site-key';
2323

24-
export function getFakeApp(): FirebaseApp {
24+
export function getFakeApp(overrides: Record<string, any> = {}): FirebaseApp {
2525
return {
2626
name: 'appName',
2727
options: {
@@ -37,7 +37,8 @@ export function getFakeApp(): FirebaseApp {
3737
delete: async () => {},
3838
// This won't be used in tests.
3939
// eslint-disable-next-line @typescript-eslint/no-explicit-any
40-
appCheck: null as any
40+
appCheck: null as any,
41+
...overrides
4142
};
4243
}
4344

0 commit comments

Comments
 (0)