Skip to content

Commit 6160497

Browse files
hsubox76Feiyang1
andauthored
Implement ReCaptchaEnterprise for App Check (#5595)
* Add recaptcha enterprise provider * start on app-check-compat * Correct Enterprise URL * prettier * Add changeset * start on app-check-compat * Add enterprise endpoint * Fix tests * update index.d.ts * Add more tests * Update .changeset/ten-impalas-wink.md Co-authored-by: Feiyang <[email protected]> * Address PR comments * Update doc comment * Address doc-related PR comments * Formatting pass * update comments Co-authored-by: Feiyang <[email protected]> Co-authored-by: Feiyang1 <[email protected]>
1 parent 31bd6f2 commit 6160497

File tree

17 files changed

+536
-180
lines changed

17 files changed

+536
-180
lines changed

.changeset/ten-impalas-wink.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@firebase/app-check': minor
3+
'@firebase/app-check-compat': minor
4+
'firebase': minor
5+
---
6+
7+
Add ReCAPTCHA Enterprise as an attestation option for App Check.

common/api-review/app-check.api.md

+105-94
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,105 @@
1-
## API Report File for "@firebase/app-check"
2-
3-
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4-
5-
```ts
6-
7-
import { FirebaseApp } from '@firebase/app';
8-
import { PartialObserver } from '@firebase/util';
9-
import { Unsubscribe } from '@firebase/util';
10-
11-
// @public
12-
export interface AppCheck {
13-
app: FirebaseApp;
14-
}
15-
16-
// @internal (undocumented)
17-
export type _AppCheckComponentName = 'app-check';
18-
19-
// @internal (undocumented)
20-
export type _AppCheckInternalComponentName = 'app-check-internal';
21-
22-
// @public
23-
export interface AppCheckOptions {
24-
isTokenAutoRefreshEnabled?: boolean;
25-
provider: CustomProvider | ReCaptchaV3Provider;
26-
}
27-
28-
// @public
29-
export interface AppCheckToken {
30-
readonly expireTimeMillis: number;
31-
// (undocumented)
32-
readonly token: string;
33-
}
34-
35-
// @public
36-
export type AppCheckTokenListener = (token: AppCheckTokenResult) => void;
37-
38-
// @public
39-
export interface AppCheckTokenResult {
40-
readonly token: string;
41-
}
42-
43-
// Warning: (ae-forgotten-export) The symbol "AppCheckProvider" needs to be exported by the entry point index.d.ts
44-
//
45-
// @public
46-
export class CustomProvider implements AppCheckProvider {
47-
constructor(_customProviderOptions: CustomProviderOptions);
48-
// Warning: (ae-forgotten-export) The symbol "AppCheckTokenInternal" needs to be exported by the entry point index.d.ts
49-
//
50-
// @internal (undocumented)
51-
getToken(): Promise<AppCheckTokenInternal>;
52-
// @internal (undocumented)
53-
initialize(app: FirebaseApp): void;
54-
// @internal (undocumented)
55-
isEqual(otherProvider: unknown): boolean;
56-
}
57-
58-
// @public
59-
export interface CustomProviderOptions {
60-
getToken: () => Promise<AppCheckToken>;
61-
}
62-
63-
// @public
64-
export function getToken(appCheckInstance: AppCheck, forceRefresh?: boolean): Promise<AppCheckTokenResult>;
65-
66-
// @public
67-
export function initializeAppCheck(app: FirebaseApp | undefined, options: AppCheckOptions): AppCheck;
68-
69-
// @public
70-
export function onTokenChanged(appCheckInstance: AppCheck, observer: PartialObserver<AppCheckTokenResult>): Unsubscribe;
71-
72-
// @public
73-
export function onTokenChanged(appCheckInstance: AppCheck, onNext: (tokenResult: AppCheckTokenResult) => void, onError?: (error: Error) => void, onCompletion?: () => void): Unsubscribe;
74-
75-
export { PartialObserver }
76-
77-
// @public
78-
export class ReCaptchaV3Provider implements AppCheckProvider {
79-
constructor(_siteKey: string);
80-
// @internal
81-
getToken(): Promise<AppCheckTokenInternal>;
82-
// @internal (undocumented)
83-
initialize(app: FirebaseApp): void;
84-
// @internal (undocumented)
85-
isEqual(otherProvider: unknown): boolean;
86-
}
87-
88-
// @public
89-
export function setTokenAutoRefreshEnabled(appCheckInstance: AppCheck, isTokenAutoRefreshEnabled: boolean): void;
90-
91-
export { Unsubscribe }
92-
93-
94-
```
1+
## API Report File for "@firebase/app-check"
2+
3+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4+
5+
```ts
6+
7+
import { FirebaseApp } from '@firebase/app';
8+
import { PartialObserver } from '@firebase/util';
9+
import { Unsubscribe } from '@firebase/util';
10+
11+
// @public
12+
export interface AppCheck {
13+
app: FirebaseApp;
14+
}
15+
16+
// @internal (undocumented)
17+
export type _AppCheckComponentName = 'app-check';
18+
19+
// @internal (undocumented)
20+
export type _AppCheckInternalComponentName = 'app-check-internal';
21+
22+
// @public
23+
export interface AppCheckOptions {
24+
isTokenAutoRefreshEnabled?: boolean;
25+
provider: CustomProvider | ReCaptchaV3Provider | ReCaptchaEnterpriseProvider;
26+
}
27+
28+
// @public
29+
export interface AppCheckToken {
30+
readonly expireTimeMillis: number;
31+
// (undocumented)
32+
readonly token: string;
33+
}
34+
35+
// @public
36+
export type AppCheckTokenListener = (token: AppCheckTokenResult) => void;
37+
38+
// @public
39+
export interface AppCheckTokenResult {
40+
readonly token: string;
41+
}
42+
43+
// Warning: (ae-forgotten-export) The symbol "AppCheckProvider" needs to be exported by the entry point index.d.ts
44+
//
45+
// @public
46+
export class CustomProvider implements AppCheckProvider {
47+
constructor(_customProviderOptions: CustomProviderOptions);
48+
// Warning: (ae-forgotten-export) The symbol "AppCheckTokenInternal" needs to be exported by the entry point index.d.ts
49+
//
50+
// @internal (undocumented)
51+
getToken(): Promise<AppCheckTokenInternal>;
52+
// @internal (undocumented)
53+
initialize(app: FirebaseApp): void;
54+
// @internal (undocumented)
55+
isEqual(otherProvider: unknown): boolean;
56+
}
57+
58+
// @public
59+
export interface CustomProviderOptions {
60+
getToken: () => Promise<AppCheckToken>;
61+
}
62+
63+
// @public
64+
export function getToken(appCheckInstance: AppCheck, forceRefresh?: boolean): Promise<AppCheckTokenResult>;
65+
66+
// @public
67+
export function initializeAppCheck(app: FirebaseApp | undefined, options: AppCheckOptions): AppCheck;
68+
69+
// @public
70+
export function onTokenChanged(appCheckInstance: AppCheck, observer: PartialObserver<AppCheckTokenResult>): Unsubscribe;
71+
72+
// @public
73+
export function onTokenChanged(appCheckInstance: AppCheck, onNext: (tokenResult: AppCheckTokenResult) => void, onError?: (error: Error) => void, onCompletion?: () => void): Unsubscribe;
74+
75+
export { PartialObserver }
76+
77+
// @public
78+
export class ReCaptchaEnterpriseProvider implements AppCheckProvider {
79+
constructor(_siteKey: string);
80+
// @internal
81+
getToken(): Promise<AppCheckTokenInternal>;
82+
// @internal (undocumented)
83+
initialize(app: FirebaseApp): void;
84+
// @internal (undocumented)
85+
isEqual(otherProvider: unknown): boolean;
86+
}
87+
88+
// @public
89+
export class ReCaptchaV3Provider implements AppCheckProvider {
90+
constructor(_siteKey: string);
91+
// @internal
92+
getToken(): Promise<AppCheckTokenInternal>;
93+
// @internal (undocumented)
94+
initialize(app: FirebaseApp): void;
95+
// @internal (undocumented)
96+
isEqual(otherProvider: unknown): boolean;
97+
}
98+
99+
// @public
100+
export function setTokenAutoRefreshEnabled(appCheckInstance: AppCheck, isTokenAutoRefreshEnabled: boolean): void;
101+
102+
export { Unsubscribe }
103+
104+
105+
```

packages/app-check-compat/src/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ import {
2828
} from '@firebase/component';
2929
import { AppCheckService } from './service';
3030
import { FirebaseAppCheck } from '@firebase/app-check-types';
31-
import { ReCaptchaV3Provider, CustomProvider } from '@firebase/app-check';
31+
import {
32+
ReCaptchaV3Provider,
33+
ReCaptchaEnterpriseProvider,
34+
CustomProvider
35+
} from '@firebase/app-check';
3236

3337
const factory: InstanceFactory<'appCheck-compat'> = (
3438
container: ComponentContainer
@@ -46,6 +50,7 @@ export function registerAppCheck(): void {
4650
factory,
4751
ComponentType.PUBLIC
4852
).setServiceProps({
53+
ReCaptchaEnterpriseProvider,
4954
ReCaptchaV3Provider,
5055
CustomProvider
5156
})

packages/app-check-compat/src/service.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
CustomProvider,
2727
initializeAppCheck,
2828
ReCaptchaV3Provider,
29+
ReCaptchaEnterpriseProvider,
2930
setTokenAutoRefreshEnabled as setTokenAutoRefreshEnabledExp,
3031
getToken as getTokenExp,
3132
onTokenChanged as onTokenChangedExp
@@ -43,10 +44,14 @@ export class AppCheckService
4344
siteKeyOrProvider: string | AppCheckProvider,
4445
isTokenAutoRefreshEnabled?: boolean
4546
): void {
46-
let provider: ReCaptchaV3Provider | CustomProvider;
47+
let provider:
48+
| ReCaptchaV3Provider
49+
| CustomProvider
50+
| ReCaptchaEnterpriseProvider;
4751
if (typeof siteKeyOrProvider === 'string') {
4852
provider = new ReCaptchaV3Provider(siteKeyOrProvider);
4953
} else if (
54+
siteKeyOrProvider instanceof ReCaptchaEnterpriseProvider ||
5055
siteKeyOrProvider instanceof ReCaptchaV3Provider ||
5156
siteKeyOrProvider instanceof CustomProvider
5257
) {

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

+39-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ import * as internalApi from './internal-api';
4141
import * as indexeddb from './indexeddb';
4242
import * as debug from './debug';
4343
import { deleteApp, FirebaseApp } from '@firebase/app';
44-
import { CustomProvider, ReCaptchaV3Provider } from './providers';
44+
import {
45+
CustomProvider,
46+
ReCaptchaEnterpriseProvider,
47+
ReCaptchaV3Provider
48+
} from './providers';
4549
import { AppCheckService } from './factory';
4650
import { AppCheckToken } from './public-types';
4751
import { getDebugToken } from './debug';
@@ -83,6 +87,16 @@ describe('api', () => {
8387
})
8488
).to.throw(/appCheck\/already-initialized/);
8589
});
90+
it('can only be called once (if given different ReCaptchaEnterpriseProviders)', () => {
91+
initializeAppCheck(app, {
92+
provider: new ReCaptchaEnterpriseProvider(FAKE_SITE_KEY)
93+
});
94+
expect(() =>
95+
initializeAppCheck(app, {
96+
provider: new ReCaptchaEnterpriseProvider(FAKE_SITE_KEY + 'X')
97+
})
98+
).to.throw(/appCheck\/already-initialized/);
99+
});
86100
it('can only be called once (if given different CustomProviders)', () => {
87101
initializeAppCheck(app, {
88102
provider: new CustomProvider({
@@ -107,6 +121,16 @@ describe('api', () => {
107121
})
108122
).to.equal(appCheckInstance);
109123
});
124+
it('can be called multiple times (if given equivalent ReCaptchaEnterpriseProviders)', () => {
125+
const appCheckInstance = initializeAppCheck(app, {
126+
provider: new ReCaptchaEnterpriseProvider(FAKE_SITE_KEY)
127+
});
128+
expect(
129+
initializeAppCheck(app, {
130+
provider: new ReCaptchaEnterpriseProvider(FAKE_SITE_KEY)
131+
})
132+
).to.equal(appCheckInstance);
133+
});
110134
it('can be called multiple times (if given equivalent CustomProviders)', () => {
111135
const appCheckInstance = initializeAppCheck(app, {
112136
provider: new CustomProvider({
@@ -166,7 +190,7 @@ describe('api', () => {
166190
});
167191

168192
it('initialize reCAPTCHA when a ReCaptchaV3Provider is provided', () => {
169-
const initReCAPTCHAStub = stub(reCAPTCHA, 'initialize').returns(
193+
const initReCAPTCHAStub = stub(reCAPTCHA, 'initializeV3').returns(
170194
Promise.resolve({} as any)
171195
);
172196
initializeAppCheck(app, {
@@ -178,6 +202,19 @@ describe('api', () => {
178202
);
179203
});
180204

205+
it('initialize reCAPTCHA when a ReCaptchaEnterpriseProvider is provided', () => {
206+
const initReCAPTCHAStub = stub(reCAPTCHA, 'initializeEnterprise').returns(
207+
Promise.resolve({} as any)
208+
);
209+
initializeAppCheck(app, {
210+
provider: new ReCaptchaEnterpriseProvider(FAKE_SITE_KEY)
211+
});
212+
expect(initReCAPTCHAStub).to.have.been.calledWithExactly(
213+
app,
214+
FAKE_SITE_KEY
215+
);
216+
});
217+
181218
it('sets activated to true', () => {
182219
expect(getState(app).activated).to.equal(false);
183220
initializeAppCheck(app, {

packages/app-check/src/api.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ declare module '@firebase/component' {
4343
}
4444
}
4545

46-
export { ReCaptchaV3Provider, CustomProvider } from './providers';
46+
export {
47+
ReCaptchaV3Provider,
48+
CustomProvider,
49+
ReCaptchaEnterpriseProvider
50+
} from './providers';
4751

4852
/**
4953
* Activate App Check for the given app. Can be called only once per app.

0 commit comments

Comments
 (0)