Skip to content

Commit f5de2a7

Browse files
committed
Add isTokenAutoRefreshEnabled setting
1 parent e123f24 commit f5de2a7

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 {
@@ -28,8 +28,12 @@ import { Provider } from '@firebase/component';
2828

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

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

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

296296
it('starts proactively refreshing token after adding the first listener', () => {
297297
const listener = (): void => {};
298+
setState(app, { ...getState(app), isTokenAutoRefreshEnabled: true });
298299
expect(getState(app).tokenListeners.length).to.equal(0);
299300
expect(getState(app).tokenRefresher).to.equal(undefined);
300301

@@ -389,6 +390,7 @@ describe('internal api', () => {
389390

390391
it('should stop proactively refreshing token after deleting the last listener', () => {
391392
const listener = (): void => {};
393+
setState(app, { ...getState(app), isTokenAutoRefreshEnabled: true });
392394

393395
addTokenListener(app, fakePlatformLoggingProvider, listener);
394396
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
@@ -183,7 +183,12 @@ export function addTokenListener(
183183
newState.tokenRefresher = tokenRefresher;
184184
}
185185

186-
if (!newState.tokenRefresher.isRunning()) {
186+
// Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
187+
// is not true.
188+
if (
189+
!newState.tokenRefresher.isRunning() &&
190+
state.isTokenAutoRefreshEnabled === true
191+
) {
187192
newState.tokenRefresher.start();
188193
}
189194

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
@@ -27,7 +27,7 @@ import {
2727

2828
export const FAKE_SITE_KEY = 'fake-site-key';
2929

30-
export function getFakeApp(): FirebaseApp {
30+
export function getFakeApp(overrides: Record<string, any> = {}): FirebaseApp {
3131
return {
3232
name: 'appName',
3333
options: {
@@ -43,7 +43,8 @@ export function getFakeApp(): FirebaseApp {
4343
delete: async () => {},
4444
// This won't be used in tests.
4545
// eslint-disable-next-line @typescript-eslint/no-explicit-any
46-
appCheck: null as any
46+
appCheck: null as any,
47+
...overrides
4748
};
4849
}
4950

0 commit comments

Comments
 (0)