Skip to content

Commit ab94a82

Browse files
committed
Convert app-check to provider pattern
1 parent b97dd4e commit ab94a82

File tree

9 files changed

+224
-63
lines changed

9 files changed

+224
-63
lines changed

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

+37-10
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ import { activate, setTokenAutoRefreshEnabled } from './api';
2121
import {
2222
FAKE_SITE_KEY,
2323
getFakeApp,
24-
getFakeCustomTokenProvider
24+
getFakeCustomTokenProvider,
25+
getFakePlatformLoggingProvider
2526
} from '../test/util';
2627
import { getState } from './state';
2728
import * as reCAPTCHA from './recaptcha';
2829
import { FirebaseApp } from '@firebase/app-types';
30+
import { ReCAPTCHAProvider } from './providers';
2931

3032
describe('api', () => {
3133
describe('activate()', () => {
@@ -37,34 +39,59 @@ describe('api', () => {
3739

3840
it('sets activated to true', () => {
3941
expect(getState(app).activated).to.equal(false);
40-
activate(app, FAKE_SITE_KEY);
42+
activate(
43+
app,
44+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
45+
getFakePlatformLoggingProvider()
46+
);
4147
expect(getState(app).activated).to.equal(true);
4248
});
4349

4450
it('isTokenAutoRefreshEnabled value defaults to global setting', () => {
4551
app = getFakeApp({ automaticDataCollectionEnabled: false });
46-
activate(app, FAKE_SITE_KEY);
52+
activate(
53+
app,
54+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
55+
getFakePlatformLoggingProvider()
56+
);
4757
expect(getState(app).isTokenAutoRefreshEnabled).to.equal(false);
4858
});
4959

5060
it('sets isTokenAutoRefreshEnabled correctly, overriding global setting', () => {
5161
app = getFakeApp({ automaticDataCollectionEnabled: false });
52-
activate(app, FAKE_SITE_KEY, true);
62+
activate(
63+
app,
64+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
65+
getFakePlatformLoggingProvider(),
66+
true
67+
);
5368
expect(getState(app).isTokenAutoRefreshEnabled).to.equal(true);
5469
});
5570

5671
it('can only be called once', () => {
57-
activate(app, FAKE_SITE_KEY);
58-
expect(() => activate(app, FAKE_SITE_KEY)).to.throw(
59-
/AppCheck can only be activated once/
72+
activate(
73+
app,
74+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
75+
getFakePlatformLoggingProvider()
6076
);
77+
expect(() =>
78+
activate(
79+
app,
80+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
81+
getFakePlatformLoggingProvider()
82+
)
83+
).to.throw(/AppCheck can only be activated once/);
6184
});
6285

6386
it('initialize reCAPTCHA when a sitekey is provided', () => {
6487
const initReCAPTCHAStub = stub(reCAPTCHA, 'initialize').returns(
6588
Promise.resolve({} as any)
6689
);
67-
activate(app, FAKE_SITE_KEY);
90+
activate(
91+
app,
92+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
93+
getFakePlatformLoggingProvider()
94+
);
6895
expect(initReCAPTCHAStub).to.have.been.calledWithExactly(
6996
app,
7097
FAKE_SITE_KEY
@@ -74,8 +101,8 @@ describe('api', () => {
74101
it('does NOT initialize reCAPTCHA when a custom token provider is provided', () => {
75102
const fakeCustomTokenProvider = getFakeCustomTokenProvider();
76103
const initReCAPTCHAStub = stub(reCAPTCHA, 'initialize');
77-
activate(app, fakeCustomTokenProvider);
78-
expect(getState(app).customProvider).to.equal(fakeCustomTokenProvider);
104+
activate(app, fakeCustomTokenProvider, getFakePlatformLoggingProvider());
105+
expect(getState(app).provider).to.equal(fakeCustomTokenProvider);
79106
expect(initReCAPTCHAStub).to.have.not.been.called;
80107
});
81108
});

packages/app-check/src/api.ts

+18-11
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,24 @@
1717

1818
import { AppCheckProvider } from '@firebase/app-check-types';
1919
import { FirebaseApp } from '@firebase/app-types';
20+
import { Provider } from '@firebase/component';
2021
import { ERROR_FACTORY, AppCheckError } from './errors';
22+
import { ReCAPTCHAProvider, ReCAPTCHAProviderInternal } from './providers';
2123
import { initialize as initializeRecaptcha } from './recaptcha';
2224
import { getState, setState, AppCheckState } from './state';
2325

2426
/**
2527
*
2628
* @param app
27-
* @param siteKeyOrProvider - optional custom attestation provider
28-
* or reCAPTCHA siteKey
29+
* @param provider - optional custom attestation provider
30+
* or reCAPTCHA provider
2931
* @param isTokenAutoRefreshEnabled - if true, enables auto refresh
3032
* of appCheck token.
3133
*/
3234
export function activate(
3335
app: FirebaseApp,
34-
siteKeyOrProvider: string | AppCheckProvider,
36+
provider: AppCheckProvider,
37+
platformLoggerProvider: Provider<'platform-logger'>,
3538
isTokenAutoRefreshEnabled?: boolean
3639
): void {
3740
const state = getState(app);
@@ -42,11 +45,7 @@ export function activate(
4245
}
4346

4447
const newState: AppCheckState = { ...state, activated: true };
45-
if (typeof siteKeyOrProvider === 'string') {
46-
newState.siteKey = siteKeyOrProvider;
47-
} else {
48-
newState.customProvider = siteKeyOrProvider;
49-
}
48+
newState.provider = provider;
5049

5150
// Use value of global `automaticDataCollectionEnabled` (which
5251
// itself defaults to false if not specified in config) if
@@ -58,9 +57,17 @@ export function activate(
5857

5958
setState(app, newState);
6059

61-
// initialize reCAPTCHA if siteKey is provided
62-
if (newState.siteKey) {
63-
initializeRecaptcha(app, newState.siteKey).catch(() => {
60+
// initialize reCAPTCHA if provider is a ReCAPTCHAProvider
61+
if (newState.provider instanceof ReCAPTCHAProvider) {
62+
// Wrap public ReCAPTCHAProvider in an internal class that provides
63+
// platform logging and app.
64+
const internalProvider = new ReCAPTCHAProviderInternal(
65+
app,
66+
newState.provider.siteKey,
67+
platformLoggerProvider
68+
);
69+
setState(app, { ...newState, provider: internalProvider });
70+
initializeRecaptcha(app, internalProvider.siteKey).catch(() => {
6471
/* we don't care about the initialization result in activate() */
6572
});
6673
}

packages/app-check/src/factory.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,21 @@ import {
2626
} from './internal-api';
2727
import { Provider } from '@firebase/component';
2828

29-
export function factory(app: FirebaseApp): FirebaseAppCheck {
29+
export function factory(
30+
app: FirebaseApp,
31+
platformLoggerProvider: Provider<'platform-logger'>
32+
): FirebaseAppCheck {
3033
return {
3134
activate: (
32-
siteKeyOrProvider: string | AppCheckProvider,
35+
provider: AppCheckProvider,
3336
isTokenAutoRefreshEnabled?: boolean
34-
) => activate(app, siteKeyOrProvider, isTokenAutoRefreshEnabled),
37+
) =>
38+
activate(
39+
app,
40+
provider,
41+
platformLoggerProvider,
42+
isTokenAutoRefreshEnabled
43+
),
3544
setTokenAutoRefreshEnabled: (isTokenAutoRefreshEnabled: boolean) =>
3645
setTokenAutoRefreshEnabled(app, isTokenAutoRefreshEnabled)
3746
};

packages/app-check/src/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
AppCheckComponentName
2727
} from '@firebase/app-check-types';
2828
import { factory, internalFactory } from './factory';
29+
import { ReCAPTCHAProvider } from './providers';
2930
import { initializeDebugMode } from './debug';
3031
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
3132
import { name, version } from '../package.json';
@@ -41,10 +42,14 @@ function registerAppCheck(firebase: _FirebaseNamespace): void {
4142
container => {
4243
// getImmediate for FirebaseApp will always succeed
4344
const app = container.getProvider('app').getImmediate();
44-
return factory(app);
45+
const platformLoggerProvider = container.getProvider('platform-logger');
46+
return factory(app, platformLoggerProvider);
4547
},
4648
ComponentType.PUBLIC
4749
)
50+
.setServiceProps({
51+
ReCAPTCHAProvider
52+
})
4853
/**
4954
* AppCheck can only be initialized by explicitly calling firebase.appCheck()
5055
* We don't want firebase products that consume AppCheck to gate on AppCheck

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

+68-15
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import * as storage from './storage';
4040
import { getState, clearState, setState, getDebugState } from './state';
4141
import { AppCheckTokenListener } from '@firebase/app-check-interop-types';
4242
import { Deferred } from '@firebase/util';
43+
import { ReCAPTCHAProvider } from './providers';
4344

4445
const fakePlatformLoggingProvider = getFakePlatformLoggingProvider();
4546

@@ -74,7 +75,7 @@ describe('internal api', () => {
7475
const customTokenProvider = getFakeCustomTokenProvider();
7576
const customProviderSpy = spy(customTokenProvider, 'getToken');
7677

77-
activate(app, customTokenProvider);
78+
activate(app, customTokenProvider, fakePlatformLoggingProvider);
7879
const token = await getToken(app, fakePlatformLoggingProvider);
7980

8081
expect(customProviderSpy).to.be.called;
@@ -85,8 +86,12 @@ describe('internal api', () => {
8586
clock.restore();
8687
});
8788

88-
it('uses reCAPTCHA token to exchange for AppCheck token if no customTokenProvider is provided', async () => {
89-
activate(app, FAKE_SITE_KEY);
89+
it('uses reCAPTCHA token to exchange for AppCheck token if ReCAPTCHAProvider is provided', async () => {
90+
activate(
91+
app,
92+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
93+
fakePlatformLoggingProvider
94+
);
9095

9196
const reCAPTCHASpy = stub(reCAPTCHA, 'getToken').returns(
9297
Promise.resolve(fakeRecaptchaToken)
@@ -108,7 +113,11 @@ describe('internal api', () => {
108113

109114
it('resolves with a dummy token and an error if failed to get a token', async () => {
110115
const errorStub = stub(console, 'error');
111-
activate(app, FAKE_SITE_KEY);
116+
activate(
117+
app,
118+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
119+
fakePlatformLoggingProvider
120+
);
112121

113122
const reCAPTCHASpy = stub(reCAPTCHA, 'getToken').returns(
114123
Promise.resolve(fakeRecaptchaToken)
@@ -131,7 +140,11 @@ describe('internal api', () => {
131140
});
132141

133142
it('notifies listeners using cached token', async () => {
134-
activate(app, FAKE_SITE_KEY);
143+
activate(
144+
app,
145+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
146+
fakePlatformLoggingProvider
147+
);
135148

136149
const clock = useFakeTimers();
137150
stub(storage, 'readTokenFromStorage').returns(
@@ -156,7 +169,11 @@ describe('internal api', () => {
156169
});
157170

158171
it('notifies listeners using new token', async () => {
159-
activate(app, FAKE_SITE_KEY);
172+
activate(
173+
app,
174+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
175+
fakePlatformLoggingProvider
176+
);
160177

161178
stub(storage, 'readTokenFromStorage').returns(Promise.resolve(undefined));
162179
stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken));
@@ -180,7 +197,11 @@ describe('internal api', () => {
180197
});
181198

182199
it('ignores listeners that throw', async () => {
183-
activate(app, FAKE_SITE_KEY);
200+
activate(
201+
app,
202+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
203+
fakePlatformLoggingProvider
204+
);
184205
stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken));
185206
stub(client, 'exchangeToken').returns(
186207
Promise.resolve(fakeRecaptchaAppCheckToken)
@@ -202,7 +223,11 @@ describe('internal api', () => {
202223

203224
it('loads persisted token to memory and returns it', async () => {
204225
const clock = useFakeTimers();
205-
activate(app, FAKE_SITE_KEY);
226+
activate(
227+
app,
228+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
229+
fakePlatformLoggingProvider
230+
);
206231

207232
stub(storage, 'readTokenFromStorage').returns(
208233
Promise.resolve(fakeCachedAppCheckToken)
@@ -221,7 +246,11 @@ describe('internal api', () => {
221246
});
222247

223248
it('persists token to storage', async () => {
224-
activate(app, FAKE_SITE_KEY);
249+
activate(
250+
app,
251+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
252+
fakePlatformLoggingProvider
253+
);
225254

226255
stub(storage, 'readTokenFromStorage').returns(Promise.resolve(undefined));
227256
stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken));
@@ -239,7 +268,11 @@ describe('internal api', () => {
239268

240269
it('returns the valid token in memory without making network request', async () => {
241270
const clock = useFakeTimers();
242-
activate(app, FAKE_SITE_KEY);
271+
activate(
272+
app,
273+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
274+
fakePlatformLoggingProvider
275+
);
243276
setState(app, { ...getState(app), token: fakeRecaptchaAppCheckToken });
244277

245278
const clientStub = stub(client, 'exchangeToken');
@@ -252,7 +285,11 @@ describe('internal api', () => {
252285
});
253286

254287
it('force to get new token when forceRefresh is true', async () => {
255-
activate(app, FAKE_SITE_KEY);
288+
activate(
289+
app,
290+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
291+
fakePlatformLoggingProvider
292+
);
256293
setState(app, { ...getState(app), token: fakeRecaptchaAppCheckToken });
257294

258295
stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken));
@@ -276,7 +313,11 @@ describe('internal api', () => {
276313
debugState.enabled = true;
277314
debugState.token = new Deferred();
278315
debugState.token.resolve('my-debug-token');
279-
activate(app, FAKE_SITE_KEY);
316+
activate(
317+
app,
318+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
319+
fakePlatformLoggingProvider
320+
);
280321

281322
const token = await getToken(app, fakePlatformLoggingProvider);
282323
expect(exchangeTokenStub.args[0][0].body['debug_token']).to.equal(
@@ -330,7 +371,11 @@ describe('internal api', () => {
330371

331372
it('notifies the listener with the valid token in storage', done => {
332373
const clock = useFakeTimers();
333-
activate(app, FAKE_SITE_KEY);
374+
activate(
375+
app,
376+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
377+
fakePlatformLoggingProvider
378+
);
334379
stub(storage, 'readTokenFromStorage').returns(
335380
Promise.resolve({
336381
token: `fake-cached-app-check-token`,
@@ -364,7 +409,11 @@ describe('internal api', () => {
364409
debugState.token = new Deferred();
365410
debugState.token.resolve('my-debug-token');
366411

367-
activate(app, FAKE_SITE_KEY);
412+
activate(
413+
app,
414+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
415+
fakePlatformLoggingProvider
416+
);
368417
addTokenListener(app, fakePlatformLoggingProvider, fakeListener);
369418
});
370419

@@ -374,7 +423,11 @@ describe('internal api', () => {
374423
debugState.token = new Deferred();
375424
debugState.token.resolve('my-debug-token');
376425

377-
activate(app, FAKE_SITE_KEY);
426+
activate(
427+
app,
428+
new ReCAPTCHAProvider(FAKE_SITE_KEY),
429+
fakePlatformLoggingProvider
430+
);
378431
addTokenListener(app, fakePlatformLoggingProvider, () => {});
379432

380433
const state = getState(app);

0 commit comments

Comments
 (0)