Skip to content

Commit 66812a1

Browse files
committed
Merge branch 'master' into fei-firestore-wait
2 parents c7d3fad + d095ad3 commit 66812a1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+588
-124
lines changed

.changeset/lemon-ligers-protect.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@firebase/firestore-types': minor
3+
'@firebase/firestore': minor
4+
'firebase': minor
5+
---
6+
7+
Add mockUserToken support for Firestore.

.changeset/loud-bears-rule.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/app': patch
3+
---
4+
5+
Add AppCheck platform logging string.

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

+10-2
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/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"bugs": {
5353
"url": "https://github.com/firebase/firebase-js-sdk/issues"
5454
},
55-
"typings": "dist/index.d.ts",
55+
"typings": "dist/src/index.d.ts",
5656
"nyc": {
5757
"extension": [
5858
".ts"

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

+20-1
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

+31-2
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/client.test.ts

+17-7
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import '../test/setup';
1919
import { expect } from 'chai';
2020
import { stub, SinonStub, useFakeTimers } from 'sinon';
2121
import { FirebaseApp } from '@firebase/app-types';
22-
import { getFakeApp } from '../test/util';
22+
import { getFakeApp, getFakePlatformLoggingProvider } from '../test/util';
2323
import { getExchangeRecaptchaTokenRequest, exchangeToken } from './client';
2424
import { FirebaseError } from '@firebase/util';
2525
import { ERROR_FACTORY, AppCheckError } from './errors';
@@ -64,12 +64,18 @@ describe('client', () => {
6464
);
6565

6666
const response = await exchangeToken(
67-
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token')
67+
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token'),
68+
getFakePlatformLoggingProvider('a/1.2.3 fire-app-check/2.3.4')
6869
);
6970

71+
expect(
72+
(fetchStub.args[0][1]?.['headers'] as any)['X-Firebase-Client']
73+
).to.equal('a/1.2.3 fire-app-check/2.3.4');
74+
7075
expect(response).to.deep.equal({
7176
token: 'fake-appcheck-token',
72-
expireTimeMillis: 3600
77+
expireTimeMillis: 3600,
78+
issuedAtTimeMillis: 0
7379
});
7480
});
7581

@@ -85,7 +91,8 @@ describe('client', () => {
8591

8692
try {
8793
await exchangeToken(
88-
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token')
94+
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token'),
95+
getFakePlatformLoggingProvider()
8996
);
9097
} catch (e) {
9198
expect(e).instanceOf(FirebaseError);
@@ -113,7 +120,8 @@ describe('client', () => {
113120

114121
try {
115122
await exchangeToken(
116-
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token')
123+
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token'),
124+
getFakePlatformLoggingProvider()
117125
);
118126
} catch (e) {
119127
expect(e).instanceOf(FirebaseError);
@@ -140,7 +148,8 @@ describe('client', () => {
140148

141149
try {
142150
await exchangeToken(
143-
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token')
151+
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token'),
152+
getFakePlatformLoggingProvider()
144153
);
145154
} catch (e) {
146155
expect(e).instanceOf(FirebaseError);
@@ -173,7 +182,8 @@ describe('client', () => {
173182

174183
try {
175184
await exchangeToken(
176-
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token')
185+
getExchangeRecaptchaTokenRequest(app, 'fake-custom-token'),
186+
getFakePlatformLoggingProvider()
177187
);
178188
} catch (e) {
179189
expect(e).instanceOf(FirebaseError);

packages/app-check/src/client.ts

+21-13
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ import {
2222
} from './constants';
2323
import { FirebaseApp } from '@firebase/app-types';
2424
import { ERROR_FACTORY, AppCheckError } from './errors';
25-
import { AppCheckToken } from '@firebase/app-check-types';
26-
import { version } from '../package.json';
25+
import { Provider } from '@firebase/component';
26+
import { AppCheckTokenInternal } from './state';
2727

2828
/**
2929
* Response JSON returned from AppCheck server endpoint.
@@ -39,18 +39,24 @@ interface AppCheckRequest {
3939
body: { [key: string]: string };
4040
}
4141

42-
export async function exchangeToken({
43-
url,
44-
body
45-
}: AppCheckRequest): Promise<AppCheckToken> {
46-
const options = {
42+
export async function exchangeToken(
43+
{ url, body }: AppCheckRequest,
44+
platformLoggerProvider: Provider<'platform-logger'>
45+
): Promise<AppCheckTokenInternal> {
46+
const headers: HeadersInit = {
47+
'Content-Type': 'application/json'
48+
};
49+
// If platform logger exists, add the platform info string to the header.
50+
const platformLogger = platformLoggerProvider.getImmediate({
51+
optional: true
52+
});
53+
if (platformLogger) {
54+
headers['X-Firebase-Client'] = platformLogger.getPlatformInfoString();
55+
}
56+
const options: RequestInit = {
4757
method: 'POST',
4858
body: JSON.stringify(body),
49-
headers: {
50-
'Content-Type': 'application/json',
51-
// JS platform identifier + appCheck version only
52-
'X-Firebase-Client': `fire-js/ fire-app-check/${version}`
53-
}
59+
headers
5460
};
5561
let response;
5662
try {
@@ -89,9 +95,11 @@ export async function exchangeToken({
8995
}
9096
const timeToLiveAsNumber = Number(match[1]) * 1000;
9197

98+
const now = Date.now();
9299
return {
93100
token: responseBody.attestationToken,
94-
expireTimeMillis: Date.now() + timeToLiveAsNumber
101+
expireTimeMillis: now + timeToLiveAsNumber,
102+
issuedAtTimeMillis: now
95103
};
96104
}
97105

packages/app-check/src/factory.ts

+16-6
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,36 @@
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 {
2323
getToken,
2424
addTokenListener,
2525
removeTokenListener
2626
} from './internal-api';
27+
import { Provider } from '@firebase/component';
2728

2829
export function factory(app: FirebaseApp): FirebaseAppCheck {
2930
return {
30-
activate: (siteKeyOrProvider: string | AppCheckProvider) =>
31-
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)
3237
};
3338
}
3439

35-
export function internalFactory(app: FirebaseApp): FirebaseAppCheckInternal {
40+
export function internalFactory(
41+
app: FirebaseApp,
42+
platformLoggerProvider: Provider<'platform-logger'>
43+
): FirebaseAppCheckInternal {
3644
return {
37-
getToken: forceRefresh => getToken(app, forceRefresh),
38-
addTokenListener: listener => addTokenListener(app, listener),
45+
getToken: forceRefresh =>
46+
getToken(app, platformLoggerProvider, forceRefresh),
47+
addTokenListener: listener =>
48+
addTokenListener(app, platformLoggerProvider, listener),
3949
removeTokenListener: listener => removeTokenListener(app, listener)
4050
};
4151
}

packages/app-check/src/index.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
import { factory, internalFactory } from './factory';
2525
import { initializeDebugMode } from './debug';
2626
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
27+
import { name, version } from '../package.json';
2728

2829
const APP_CHECK_NAME: AppCheckComponentName = 'appCheck';
2930
const APP_CHECK_NAME_INTERNAL: AppCheckInternalComponentName =
@@ -49,13 +50,14 @@ function registerAppCheck(firebase: _FirebaseNamespace): void {
4950
container => {
5051
// getImmediate for FirebaseApp will always succeed
5152
const app = container.getProvider('app').getImmediate();
52-
return internalFactory(app);
53+
const platformLoggerProvider = container.getProvider('platform-logger');
54+
return internalFactory(app, platformLoggerProvider);
5355
},
5456
ComponentType.PUBLIC
5557
)
5658
);
5759

58-
// TODO: register AppCheck version with firebase.registerVersion() before BETA. We don't want to report version in EAP
60+
firebase.registerVersion(name, version);
5961
}
6062

6163
registerAppCheck(firebase as _FirebaseNamespace);

packages/app-check/src/indexeddb.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { AppCheckToken } from '@firebase/app-check-types';
1918
import { FirebaseApp } from '@firebase/app-types';
2019
import { ERROR_FACTORY, AppCheckError } from './errors';
20+
import { AppCheckTokenInternal } from './state';
2121
const DB_NAME = 'firebase-app-check-database';
2222
const DB_VERSION = 1;
2323
const STORE_NAME = 'firebase-app-check-store';
@@ -74,13 +74,13 @@ function getDBPromise(): Promise<IDBDatabase> {
7474

7575
export function readTokenFromIndexedDB(
7676
app: FirebaseApp
77-
): Promise<AppCheckToken | undefined> {
78-
return read(computeKey(app)) as Promise<AppCheckToken | undefined>;
77+
): Promise<AppCheckTokenInternal | undefined> {
78+
return read(computeKey(app)) as Promise<AppCheckTokenInternal | undefined>;
7979
}
8080

8181
export function writeTokenToIndexedDB(
8282
app: FirebaseApp,
83-
token: AppCheckToken
83+
token: AppCheckTokenInternal
8484
): Promise<void> {
8585
return write(computeKey(app), token);
8686
}

0 commit comments

Comments
 (0)