Skip to content

Commit d1cc28c

Browse files
committed
Add providers for app check
1 parent 5acfdae commit d1cc28c

File tree

12 files changed

+268
-124
lines changed

12 files changed

+268
-124
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import { PartialObserver, Unsubscribe } from '@firebase/util';
1919
import { FirebaseApp } from '@firebase/app-types';
20+
import { Provider } from '@firebase/component';
2021

2122
export interface FirebaseAppCheck {
2223
/** The `FirebaseApp` associated with this instance. */
@@ -96,6 +97,22 @@ interface ReCAPTCHAV3Provider {
9697
*/
9798
constructor(siteKey: string): void;
9899
}
100+
/*
101+
* Custom token provider.
102+
*/
103+
interface CustomProvider {
104+
/**
105+
* @param options - Options for creating the custom provider.
106+
*/
107+
constructor(options: CustomProviderOptions): void;
108+
}
109+
interface CustomProviderOptions {
110+
/**
111+
* Function to get an App Check token through a custom provider
112+
* service.
113+
*/
114+
getToken: () => Promise<AppCheckToken>;
115+
}
99116

100117
/**
101118
* The token returned from an `AppCheckProvider`.

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

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import * as client from './client';
3939
import * as storage from './storage';
4040
import * as logger from './logger';
4141
import * as util from './util';
42-
import { ReCAPTCHAV3Provider } from './providers';
42+
import { ReCaptchaV3Provider } from './providers';
4343

4444
describe('api', () => {
4545
beforeEach(() => {
@@ -56,7 +56,7 @@ describe('api', () => {
5656
expect(getState(app).activated).to.equal(false);
5757
activate(
5858
app,
59-
new ReCAPTCHAV3Provider(FAKE_SITE_KEY),
59+
new ReCaptchaV3Provider(FAKE_SITE_KEY),
6060
getFakePlatformLoggingProvider()
6161
);
6262
expect(getState(app).activated).to.equal(true);
@@ -66,7 +66,7 @@ describe('api', () => {
6666
app = getFakeApp({ automaticDataCollectionEnabled: false });
6767
activate(
6868
app,
69-
new ReCAPTCHAV3Provider(FAKE_SITE_KEY),
69+
new ReCaptchaV3Provider(FAKE_SITE_KEY),
7070
getFakePlatformLoggingProvider()
7171
);
7272
expect(getState(app).isTokenAutoRefreshEnabled).to.equal(false);
@@ -76,7 +76,7 @@ describe('api', () => {
7676
app = getFakeApp({ automaticDataCollectionEnabled: false });
7777
activate(
7878
app,
79-
new ReCAPTCHAV3Provider(FAKE_SITE_KEY),
79+
new ReCaptchaV3Provider(FAKE_SITE_KEY),
8080
getFakePlatformLoggingProvider(),
8181
true
8282
);
@@ -86,25 +86,36 @@ describe('api', () => {
8686
it('can only be called once', () => {
8787
activate(
8888
app,
89-
new ReCAPTCHAV3Provider(FAKE_SITE_KEY),
89+
new ReCaptchaV3Provider(FAKE_SITE_KEY),
9090
getFakePlatformLoggingProvider()
9191
);
9292
expect(() =>
9393
activate(
9494
app,
95-
new ReCAPTCHAV3Provider(FAKE_SITE_KEY),
95+
new ReCaptchaV3Provider(FAKE_SITE_KEY),
9696
getFakePlatformLoggingProvider()
9797
)
9898
).to.throw(/AppCheck can only be activated once/);
9999
});
100100

101-
it('initialize reCAPTCHA when a sitekey is provided', () => {
101+
it('initialize reCAPTCHA when a sitekey string is provided', () => {
102+
const initReCAPTCHAStub = stub(reCAPTCHA, 'initialize').returns(
103+
Promise.resolve({} as any)
104+
);
105+
activate(app, FAKE_SITE_KEY, getFakePlatformLoggingProvider());
106+
expect(initReCAPTCHAStub).to.have.been.calledWithExactly(
107+
app,
108+
FAKE_SITE_KEY
109+
);
110+
});
111+
112+
it('initialize reCAPTCHA when a ReCaptchaV3Provider instance is provided', () => {
102113
const initReCAPTCHAStub = stub(reCAPTCHA, 'initialize').returns(
103114
Promise.resolve({} as any)
104115
);
105116
activate(
106117
app,
107-
new ReCAPTCHAV3Provider(FAKE_SITE_KEY),
118+
new ReCaptchaV3Provider(FAKE_SITE_KEY),
108119
getFakePlatformLoggingProvider()
109120
);
110121
expect(initReCAPTCHAStub).to.have.been.calledWithExactly(
@@ -113,13 +124,22 @@ describe('api', () => {
113124
);
114125
});
115126

116-
it('does NOT initialize reCAPTCHA when a custom token provider is provided', () => {
117-
const fakeCustomTokenProvider = getFakeCustomTokenProvider();
118-
const initReCAPTCHAStub = stub(reCAPTCHA, 'initialize');
119-
activate(app, fakeCustomTokenProvider, getFakePlatformLoggingProvider());
120-
expect(getState(app).provider).to.equal(fakeCustomTokenProvider);
121-
expect(initReCAPTCHAStub).to.have.not.been.called;
122-
});
127+
it(
128+
'creates CustomProvider instance if user provides an object containing' +
129+
' a getToken() method',
130+
async () => {
131+
const fakeCustomTokenProvider = getFakeCustomTokenProvider();
132+
const initReCAPTCHAStub = stub(reCAPTCHA, 'initialize');
133+
activate(
134+
app,
135+
fakeCustomTokenProvider,
136+
getFakePlatformLoggingProvider()
137+
);
138+
const result = await getState(app).provider?.getToken();
139+
expect(result?.token).to.equal('fake-custom-app-check-token');
140+
expect(initReCAPTCHAStub).to.have.not.been.called;
141+
}
142+
);
123143
});
124144
describe('setTokenAutoRefreshEnabled()', () => {
125145
it('sets isTokenAutoRefreshEnabled correctly', () => {
@@ -177,7 +197,7 @@ describe('api', () => {
177197
const app = getFakeApp();
178198
activate(
179199
app,
180-
new ReCAPTCHAV3Provider(FAKE_SITE_KEY),
200+
new ReCaptchaV3Provider(FAKE_SITE_KEY),
181201
fakePlatformLoggingProvider,
182202
false
183203
);
@@ -226,7 +246,7 @@ describe('api', () => {
226246
const app = getFakeApp();
227247
activate(
228248
app,
229-
new ReCAPTCHAV3Provider(FAKE_SITE_KEY),
249+
new ReCaptchaV3Provider(FAKE_SITE_KEY),
230250
fakePlatformLoggingProvider,
231251
false
232252
);
@@ -276,7 +296,7 @@ describe('api', () => {
276296
const app = getFakeApp();
277297
activate(
278298
app,
279-
new ReCAPTCHAV3Provider(FAKE_SITE_KEY),
299+
new ReCaptchaV3Provider(FAKE_SITE_KEY),
280300
fakePlatformLoggingProvider,
281301
false
282302
);

packages/app-check/src/api.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ import {
2020
AppCheckTokenResult
2121
} from '@firebase/app-check-types';
2222
import { FirebaseApp } from '@firebase/app-types';
23-
import { Provider } from '@firebase/component';
2423
import { ERROR_FACTORY, AppCheckError } from './errors';
25-
import { ReCAPTCHAV3Provider, ReCAPTCHAV3ProviderInternal } from './providers';
26-
import { initialize as initializeRecaptcha } from './recaptcha';
2724
import { getState, setState, AppCheckState, ListenerType } from './state';
2825
import {
2926
getToken as getTokenInternal,
@@ -32,6 +29,7 @@ import {
3229
} from './internal-api';
3330
import { Provider } from '@firebase/component';
3431
import { ErrorFn, NextFn, PartialObserver, Unsubscribe } from '@firebase/util';
32+
import { CustomProvider, ReCaptchaV3Provider } from './providers';
3533

3634
/**
3735
*
@@ -43,7 +41,12 @@ import { ErrorFn, NextFn, PartialObserver, Unsubscribe } from '@firebase/util';
4341
*/
4442
export function activate(
4543
app: FirebaseApp,
46-
provider: AppCheckProvider,
44+
siteKeyOrProvider:
45+
| ReCaptchaV3Provider
46+
| CustomProvider
47+
// This is the old interface for users to supply a custom provider.
48+
| AppCheckProvider
49+
| string,
4750
platformLoggerProvider: Provider<'platform-logger'>,
4851
isTokenAutoRefreshEnabled?: boolean
4952
): void {
@@ -55,7 +58,22 @@ export function activate(
5558
}
5659

5760
const newState: AppCheckState = { ...state, activated: true };
58-
newState.provider = provider;
61+
62+
if (typeof siteKeyOrProvider === 'string') {
63+
newState.provider = new ReCaptchaV3Provider(siteKeyOrProvider);
64+
} else if (
65+
siteKeyOrProvider instanceof ReCaptchaV3Provider ||
66+
siteKeyOrProvider instanceof CustomProvider
67+
) {
68+
newState.provider = siteKeyOrProvider;
69+
} else {
70+
// Process "old" custom provider to avoid breaking previous users.
71+
// This was defined at beta release as simply an object with a
72+
// getToken() method.
73+
newState.provider = new CustomProvider({
74+
getToken: siteKeyOrProvider.getToken
75+
});
76+
}
5977

6078
// Use value of global `automaticDataCollectionEnabled` (which
6179
// itself defaults to false if not specified in config) if
@@ -67,20 +85,7 @@ export function activate(
6785

6886
setState(app, newState);
6987

70-
// initialize reCAPTCHA if provider is a ReCAPTCHAProvider
71-
if (newState.provider instanceof ReCAPTCHAV3Provider) {
72-
// Wrap public ReCAPTCHAProvider in an internal class that provides
73-
// platform logging and app.
74-
const internalProvider = new ReCAPTCHAV3ProviderInternal(
75-
app,
76-
newState.provider.siteKey,
77-
platformLoggerProvider
78-
);
79-
setState(app, { ...newState, provider: internalProvider });
80-
initializeRecaptcha(app, internalProvider.siteKey).catch(() => {
81-
/* we don't care about the initialization result in activate() */
82-
});
83-
}
88+
newState.provider.initialize(app, platformLoggerProvider);
8489
}
8590

8691
export function setTokenAutoRefreshEnabled(

packages/app-check/src/factory.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ export function factory(
4646
return {
4747
app,
4848
activate: (
49-
provider: AppCheckProvider,
49+
siteKeyOrProvider: AppCheckProvider | string,
5050
isTokenAutoRefreshEnabled?: boolean
5151
) =>
5252
activate(
5353
app,
54-
provider,
54+
siteKeyOrProvider,
5555
platformLoggerProvider,
5656
isTokenAutoRefreshEnabled
5757
),

packages/app-check/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
AppCheckComponentName
2727
} from '@firebase/app-check-types';
2828
import { factory, internalFactory } from './factory';
29-
import { ReCAPTCHAV3Provider } from './providers';
29+
import { ReCaptchaV3Provider, CustomProvider } from './providers';
3030
import { initializeDebugMode } from './debug';
3131
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
3232
import { name, version } from '../package.json';
@@ -48,7 +48,8 @@ function registerAppCheck(firebase: _FirebaseNamespace): void {
4848
ComponentType.PUBLIC
4949
)
5050
.setServiceProps({
51-
ReCAPTCHAV3Provider
51+
ReCaptchaV3Provider,
52+
CustomProvider
5253
})
5354
/**
5455
* AppCheck can only be initialized by explicitly calling firebase.appCheck()

0 commit comments

Comments
 (0)