Skip to content

Commit 6ad4f58

Browse files
authored
Fix app-check-compat initialization (#5089)
1 parent f6e1645 commit 6ad4f58

File tree

5 files changed

+114
-27
lines changed

5 files changed

+114
-27
lines changed

packages-exp/app-check-compat/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@
5757
"reportDir": "./coverage/node"
5858
},
5959
"esm5": "dist/index.esm.js"
60-
}
60+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { ErrorFactory, ErrorMap } from '@firebase/util';
19+
20+
export const enum AppCheckError {
21+
USE_BEFORE_ACTIVATION = 'use-before-activation'
22+
}
23+
24+
const ERRORS: ErrorMap<AppCheckError> = {
25+
[AppCheckError.USE_BEFORE_ACTIVATION]:
26+
'App Check is being used before activate() is called for FirebaseApp {$appName}. ' +
27+
'Call activate() before instantiating other Firebase services.'
28+
};
29+
30+
interface ErrorParams {
31+
[AppCheckError.USE_BEFORE_ACTIVATION]: { appName: string };
32+
}
33+
34+
export const ERROR_FACTORY = new ErrorFactory<AppCheckError, ErrorParams>(
35+
'appCheck',
36+
'AppCheck',
37+
ERRORS
38+
);

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
InstanceFactory
2828
} from '@firebase/component';
2929
import { AppCheckService } from './service';
30-
import { FirebaseAppCheck } from '../../../packages/app-check-types';
30+
import { FirebaseAppCheck } from '@firebase/app-check-types';
3131

3232
declare module '@firebase/component' {
3333
interface NameServiceMapping {
@@ -40,11 +40,8 @@ const factory: InstanceFactory<'appCheck-compat'> = (
4040
) => {
4141
// Dependencies
4242
const app = container.getProvider('app-compat').getImmediate();
43-
const appCheckServiceExp = container
44-
.getProvider('app-check-exp')
45-
.getImmediate();
4643

47-
return new AppCheckService(app as FirebaseApp, appCheckServiceExp);
44+
return new AppCheckService(app as FirebaseApp);
4845
};
4946

5047
export function registerAppCheck(): void {

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

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,30 @@ import { firebase, FirebaseApp } from '@firebase/app-compat';
2020
import * as appCheckExp from '@firebase/app-check-exp';
2121
import { stub, match, SinonStub } from 'sinon';
2222
import * as sinonChai from 'sinon-chai';
23-
import { CustomProvider, ReCaptchaV3Provider } from '@firebase/app-check-exp';
24-
import { AppCheckTokenResult } from '../../../packages/app-check-types';
25-
import { PartialObserver } from '../../../packages/util/dist';
23+
import {
24+
AppCheck,
25+
CustomProvider,
26+
ReCaptchaV3Provider
27+
} from '@firebase/app-check-exp';
28+
import { AppCheckTokenResult } from '@firebase/app-check-types';
29+
import { PartialObserver } from '@firebase/util';
30+
import { AppCheckError } from './errors';
2631

2732
use(sinonChai);
2833

2934
function createTestService(app: FirebaseApp): AppCheckService {
30-
return new AppCheckService(
31-
app,
32-
appCheckExp.initializeAppCheck(app, {
33-
provider: new ReCaptchaV3Provider('fake-site-key')
34-
})
35-
);
35+
return new AppCheckService(app);
36+
}
37+
38+
function createActivatedTestService(app: FirebaseApp): AppCheckService {
39+
const service = new AppCheckService(app);
40+
const initializeAppCheckStub = stub(
41+
appCheckExp,
42+
'initializeAppCheck'
43+
).returns({} as AppCheck);
44+
service.activate('a-site-key');
45+
initializeAppCheckStub.restore();
46+
return service;
3647
}
3748

3849
describe('Firebase App Check > Service', () => {
@@ -57,7 +68,7 @@ describe('Firebase App Check > Service', () => {
5768
'ReCaptchaV3Provider',
5869
() => {
5970
const initializeAppCheckStub = stub(appCheckExp, 'initializeAppCheck');
60-
service = new AppCheckService(app, {} as appCheckExp.AppCheck);
71+
service = new AppCheckService(app);
6172
service.activate('my_site_key');
6273
expect(initializeAppCheckStub).to.be.calledWith(app, {
6374
provider: match.instanceOf(ReCaptchaV3Provider),
@@ -72,7 +83,7 @@ describe('Firebase App Check > Service', () => {
7283
' a CustomProvider',
7384
() => {
7485
const initializeAppCheckStub = stub(appCheckExp, 'initializeAppCheck');
75-
service = new AppCheckService(app, {} as appCheckExp.AppCheck);
86+
service = new AppCheckService(app);
7687
const customGetTokenStub = stub();
7788
service.activate({
7889
getToken: customGetTokenStub
@@ -97,7 +108,7 @@ describe('Firebase App Check > Service', () => {
97108
appCheckExp,
98109
'setTokenAutoRefreshEnabled'
99110
);
100-
service = createTestService(app);
111+
service = createActivatedTestService(app);
101112
service.setTokenAutoRefreshEnabled(true);
102113
expect(setTokenAutoRefreshEnabledStub).to.be.calledWith(
103114
service._delegate,
@@ -107,7 +118,7 @@ describe('Firebase App Check > Service', () => {
107118
});
108119

109120
it('getToken() calls modular getToken()', async () => {
110-
service = createTestService(app);
121+
service = createActivatedTestService(app);
111122
const getTokenStub = stub(appCheckExp, 'getToken');
112123
await service.getToken(true);
113124
expect(getTokenStub).to.be.calledWith(service._delegate, true);
@@ -116,7 +127,7 @@ describe('Firebase App Check > Service', () => {
116127

117128
it('onTokenChanged() calls modular onTokenChanged() with observer', () => {
118129
const onTokenChangedStub = stub(appCheckExp, 'onTokenChanged');
119-
service = createTestService(app);
130+
service = createActivatedTestService(app);
120131
const observer: PartialObserver<AppCheckTokenResult> = {
121132
next: stub(),
122133
error: stub()
@@ -128,7 +139,7 @@ describe('Firebase App Check > Service', () => {
128139

129140
it('onTokenChanged() calls modular onTokenChanged() with next/error fns', () => {
130141
const onTokenChangedStub = stub(appCheckExp, 'onTokenChanged');
131-
service = createTestService(app);
142+
service = createActivatedTestService(app);
132143
const nextFn = stub();
133144
const errorFn = stub();
134145
service.onTokenChanged(nextFn, errorFn);
@@ -139,4 +150,25 @@ describe('Firebase App Check > Service', () => {
139150
);
140151
onTokenChangedStub.restore();
141152
});
153+
154+
it('setTokenAutoRefreshEnabled() throws if activate() has not been called', async () => {
155+
service = createTestService(app);
156+
expect(() => service.setTokenAutoRefreshEnabled(true)).to.throw(
157+
AppCheckError.USE_BEFORE_ACTIVATION
158+
);
159+
});
160+
161+
it('getToken() throws if activate() has not been called', async () => {
162+
service = createTestService(app);
163+
expect(() => service.getToken(true)).to.throw(
164+
AppCheckError.USE_BEFORE_ACTIVATION
165+
);
166+
});
167+
168+
it('onTokenChanged() throws if activate() has not been called', async () => {
169+
service = createTestService(app);
170+
expect(() => service.onTokenChanged(() => {})).to.throw(
171+
AppCheckError.USE_BEFORE_ACTIVATION
172+
);
173+
});
142174
});

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

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ import {
3131
onTokenChanged as onTokenChangedExp
3232
} from '@firebase/app-check-exp';
3333
import { PartialObserver, Unsubscribe } from '@firebase/util';
34+
import { ERROR_FACTORY, AppCheckError } from './errors';
35+
36+
export class AppCheckService
37+
implements FirebaseAppCheck, Omit<_FirebaseService, '_delegate'>
38+
{
39+
_delegate?: AppCheckServiceExp;
40+
constructor(public app: FirebaseApp) {}
3441

35-
export class AppCheckService implements FirebaseAppCheck, _FirebaseService {
36-
constructor(
37-
public app: FirebaseApp,
38-
readonly _delegate: AppCheckServiceExp
39-
) {}
4042
activate(
4143
siteKeyOrProvider: string | AppCheckProvider,
4244
isTokenAutoRefreshEnabled?: boolean
@@ -47,24 +49,42 @@ export class AppCheckService implements FirebaseAppCheck, _FirebaseService {
4749
} else {
4850
provider = new CustomProvider({ getToken: siteKeyOrProvider.getToken });
4951
}
50-
initializeAppCheck(this.app, {
52+
this._delegate = initializeAppCheck(this.app, {
5153
provider,
5254
isTokenAutoRefreshEnabled
5355
});
5456
}
57+
5558
setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled: boolean): void {
59+
if (!this._delegate) {
60+
throw ERROR_FACTORY.create(AppCheckError.USE_BEFORE_ACTIVATION, {
61+
appName: this.app.name
62+
});
63+
}
5664
setTokenAutoRefreshEnabledExp(this._delegate, isTokenAutoRefreshEnabled);
5765
}
66+
5867
getToken(forceRefresh?: boolean): Promise<AppCheckTokenResult> {
68+
if (!this._delegate) {
69+
throw ERROR_FACTORY.create(AppCheckError.USE_BEFORE_ACTIVATION, {
70+
appName: this.app.name
71+
});
72+
}
5973
return getTokenExp(this._delegate, forceRefresh);
6074
}
75+
6176
onTokenChanged(
6277
onNextOrObserver:
6378
| PartialObserver<AppCheckTokenResult>
6479
| ((tokenResult: AppCheckTokenResult) => void),
6580
onError?: (error: Error) => void,
6681
onCompletion?: () => void
6782
): Unsubscribe {
83+
if (!this._delegate) {
84+
throw ERROR_FACTORY.create(AppCheckError.USE_BEFORE_ACTIVATION, {
85+
appName: this.app.name
86+
});
87+
}
6888
return onTokenChangedExp(
6989
this._delegate,
7090
/**

0 commit comments

Comments
 (0)