Skip to content

Commit 9d0042f

Browse files
Merge branch 'master' into mrschmidt/storagenode
2 parents 3c9279c + 870dd5e commit 9d0042f

File tree

20 files changed

+566
-70
lines changed

20 files changed

+566
-70
lines changed

.changeset/empty-countries-run.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@firebase/app-check': minor
3+
'@firebase/app-check-types': minor
4+
'firebase': minor
5+
---
6+
7+
Added `getToken()` and `onTokenChanged` methods to App Check.

packages-exp/auth-exp/src/platform_browser/persistence/indexed_db.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,10 @@ export function _openDatabase(): Promise<IDBDatabase> {
113113
// https://github.com/firebase/firebase-js-sdk/issues/634
114114

115115
if (!db.objectStoreNames.contains(DB_OBJECTSTORE_NAME)) {
116+
// Need to close the database or else you get a `blocked` event
117+
db.close();
116118
await _deleteDatabase();
117-
return _openDatabase();
119+
resolve(await _openDatabase());
118120
} else {
119121
resolve(db);
120122
}

packages-exp/auth-exp/test/integration/webdriver/persistence.test.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ async function testPersistedUser() {
4747
};
4848
}
4949

50-
browserDescribe('WebDriver persistence test', driver => {
50+
browserDescribe('WebDriver persistence test', (driver, browser) => {
5151
const fullPersistenceKey = `firebase:authUser:${API_KEY}:[DEFAULT]`;
5252
context('default persistence hierarchy (indexedDB > localStorage)', () => {
5353
it('stores user in indexedDB by default', async () => {
@@ -381,6 +381,12 @@ browserDescribe('WebDriver persistence test', driver => {
381381
});
382382

383383
it('stays logged in when switching from legacy SDK and then back (no indexedDB support)', async () => {
384+
// Skip this test if running in Firefox. The Legacy SDK incorrectly
385+
// implements the db delete + reopen workaround for Firefox.
386+
if (browser === 'firefox') {
387+
return;
388+
}
389+
384390
await driver.webDriver.navigate().refresh();
385391
// Simulate browsers that do not support indexedDB.
386392
await driver.webDriver.executeScript('delete window.indexedDB');

packages-exp/auth-exp/test/integration/webdriver/util/test_runner.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { AuthDriver } from './auth_driver';
3131
*/
3232

3333
interface TempSuite {
34-
generator: (driver: AuthDriver) => void;
34+
generator: (driver: AuthDriver, browser: string) => void;
3535
title: string;
3636
}
3737

@@ -45,7 +45,7 @@ const SUITES: TempSuite[] = [];
4545
/** Main entry point for all WebDriver tests */
4646
export function browserDescribe(
4747
title: string,
48-
generator: (driver: AuthDriver) => void
48+
generator: (driver: AuthDriver, browser: string) => void
4949
): void {
5050
SUITES.push({
5151
title,
@@ -75,7 +75,7 @@ setTimeout(() => {
7575
});
7676

7777
for (const { title, generator } of SUITES) {
78-
describe(title, () => generator(DRIVER));
78+
describe(title, () => generator(DRIVER, browser));
7979
}
8080
});
8181
}

packages-exp/functions-exp/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
"dependencies": {
5353
"@firebase/component": "0.5.3",
5454
"@firebase/messaging-types": "0.5.0",
55+
"@firebase/auth-interop-types": "0.1.6",
56+
"@firebase/app-check-interop-types": "0.1.0",
5557
"@firebase/util": "1.1.0",
5658
"node-fetch": "2.6.1",
5759
"tslib": "^2.1.0"

packages-exp/functions-exp/src/config.ts

+5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ import {
2424
InstanceFactory
2525
} from '@firebase/component';
2626
import { FUNCTIONS_TYPE } from './constants';
27+
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
2728

29+
const APP_CHECK_INTERNAL_NAME: AppCheckInternalComponentName =
30+
'app-check-internal';
2831
export function registerFunctions(fetchImpl: typeof fetch): void {
2932
const factory: InstanceFactory<'functions-exp'> = (
3033
container: ComponentContainer,
@@ -34,12 +37,14 @@ export function registerFunctions(fetchImpl: typeof fetch): void {
3437
const app = container.getProvider('app-exp').getImmediate();
3538
const authProvider = container.getProvider('auth-internal');
3639
const messagingProvider = container.getProvider('messaging');
40+
const appCheckProvider = container.getProvider(APP_CHECK_INTERNAL_NAME);
3741

3842
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3943
return new FunctionsService(
4044
app,
4145
authProvider,
4246
messagingProvider,
47+
appCheckProvider,
4348
regionOrCustomDomain,
4449
fetchImpl
4550
);

packages-exp/functions-exp/src/context.ts

+31-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ import {
2424
} from '@firebase/messaging-types';
2525

2626
import { Provider } from '@firebase/component';
27+
import {
28+
AppCheckInternalComponentName,
29+
FirebaseAppCheckInternal
30+
} from '@firebase/app-check-interop-types';
2731

2832
/**
2933
* The metadata that should be supplied with function calls.
@@ -32,6 +36,7 @@ import { Provider } from '@firebase/component';
3236
export interface Context {
3337
authToken?: string;
3438
messagingToken?: string;
39+
appCheckToken: string | null;
3540
}
3641

3742
/**
@@ -41,9 +46,11 @@ export interface Context {
4146
export class ContextProvider {
4247
private auth: FirebaseAuthInternal | null = null;
4348
private messaging: FirebaseMessaging | null = null;
49+
private appCheck: FirebaseAppCheckInternal | null = null;
4450
constructor(
4551
authProvider: Provider<FirebaseAuthInternalName>,
46-
messagingProvider: Provider<FirebaseMessagingName>
52+
messagingProvider: Provider<FirebaseMessagingName>,
53+
appCheckProvider: Provider<AppCheckInternalComponentName>
4754
) {
4855
this.auth = authProvider.getImmediate({ optional: true });
4956
this.messaging = messagingProvider.getImmediate({
@@ -67,6 +74,15 @@ export class ContextProvider {
6774
}
6875
);
6976
}
77+
78+
if (!this.appCheck) {
79+
appCheckProvider.get().then(
80+
appCheck => (this.appCheck = appCheck),
81+
() => {
82+
/* get() never rejects */
83+
}
84+
);
85+
}
7086
}
7187

7288
async getAuthToken(): Promise<string | undefined> {
@@ -103,9 +119,22 @@ export class ContextProvider {
103119
}
104120
}
105121

122+
async getAppCheckToken(): Promise<string | null> {
123+
if (this.appCheck) {
124+
const result = await this.appCheck.getToken();
125+
// If getToken() fails, it will still return a dummy token that also has
126+
// an error field containing the error message. We will send any token
127+
// provided here and show an error if/when it is rejected by the functions
128+
// endpoint.
129+
return result.token;
130+
}
131+
return null;
132+
}
133+
106134
async getContext(): Promise<Context> {
107135
const authToken = await this.getAuthToken();
108136
const messagingToken = await this.getMessagingToken();
109-
return { authToken, messagingToken };
137+
const appCheckToken = await this.getAppCheckToken();
138+
return { authToken, messagingToken, appCheckToken };
110139
}
111140
}

packages-exp/functions-exp/src/service.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { encode, decode } from './serializer';
2727
import { Provider } from '@firebase/component';
2828
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
2929
import { FirebaseMessagingName } from '@firebase/messaging-types';
30+
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
3031

3132
export const DEFAULT_REGION = 'us-central1';
3233

@@ -86,10 +87,15 @@ export class FunctionsService implements _FirebaseService {
8687
readonly app: FirebaseApp,
8788
authProvider: Provider<FirebaseAuthInternalName>,
8889
messagingProvider: Provider<FirebaseMessagingName>,
90+
appCheckProvider: Provider<AppCheckInternalComponentName>,
8991
regionOrCustomDomain: string = DEFAULT_REGION,
9092
readonly fetchImpl: typeof fetch
9193
) {
92-
this.contextProvider = new ContextProvider(authProvider, messagingProvider);
94+
this.contextProvider = new ContextProvider(
95+
authProvider,
96+
messagingProvider,
97+
appCheckProvider
98+
);
9399
// Cancels all ongoing requests when resolved.
94100
this.cancelAllRequests = new Promise(resolve => {
95101
this.deleteService = () => {
@@ -234,6 +240,9 @@ async function call(
234240
if (context.messagingToken) {
235241
headers['Firebase-Instance-ID-Token'] = context.messagingToken;
236242
}
243+
if (context.appCheckToken !== null) {
244+
headers['X-Firebase-AppCheck'] = context.appCheckToken;
245+
}
237246

238247
// Default timeout to 70s, but let the options override it.
239248
const timeout = options.timeout || 70000;

packages-exp/functions-exp/test/utils.ts

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { FirebaseOptions, FirebaseApp } from '@firebase/app-exp';
1919
import { Provider, ComponentContainer } from '@firebase/component';
2020
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
2121
import { FirebaseMessagingName } from '@firebase/messaging-types';
22+
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
2223
import { FunctionsService } from '../src/service';
2324
import { useFunctionsEmulator } from '../src/api';
2425
import nodeFetch from 'node-fetch';
@@ -51,6 +52,10 @@ export function createTestService(
5152
messagingProvider = new Provider<FirebaseMessagingName>(
5253
'messaging',
5354
new ComponentContainer('test')
55+
),
56+
appCheckProvider = new Provider<AppCheckInternalComponentName>(
57+
'app-check-internal',
58+
new ComponentContainer('test')
5459
)
5560
): FunctionsService {
5661
const fetchImpl: typeof fetch =
@@ -59,6 +64,7 @@ export function createTestService(
5964
app,
6065
authProvider,
6166
messagingProvider,
67+
appCheckProvider,
6268
region,
6369
fetchImpl
6470
);

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

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

18+
import { PartialObserver, Unsubscribe } from '@firebase/util';
19+
1820
export interface FirebaseAppCheck {
1921
/**
2022
* Activate AppCheck
@@ -36,6 +38,40 @@ export interface FirebaseAppCheck {
3638
* during `activate()`.
3739
*/
3840
setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled: boolean): void;
41+
42+
/**
43+
* Get the current App Check token. Attaches to the most recent
44+
* in-flight request if one is present. Returns null if no token
45+
* is present and no token requests are in flight.
46+
*
47+
* @param forceRefresh - If true, will always try to fetch a fresh token.
48+
* If false, will use a cached token if found in storage.
49+
*/
50+
getToken(forceRefresh?: boolean): Promise<AppCheckTokenResult>;
51+
52+
/**
53+
* Registers a listener to changes in the token state. There can be more
54+
* than one listener registered at the same time for one or more
55+
* App Check instances. The listeners call back on the UI thread whenever
56+
* the current token associated with this App Check instance changes.
57+
*
58+
* @returns A function that unsubscribes this listener.
59+
*/
60+
onTokenChanged(observer: PartialObserver<AppCheckTokenResult>): Unsubscribe;
61+
62+
/**
63+
* Registers a listener to changes in the token state. There can be more
64+
* than one listener registered at the same time for one or more
65+
* App Check instances. The listeners call back on the UI thread whenever
66+
* the current token associated with this App Check instance changes.
67+
*
68+
* @returns A function that unsubscribes this listener.
69+
*/
70+
onTokenChanged(
71+
onNext: (tokenResult: AppCheckTokenResult) => void,
72+
onError?: (error: Error) => void,
73+
onCompletion?: () => void
74+
): Unsubscribe;
3975
}
4076

4177
/**
@@ -64,6 +100,16 @@ interface AppCheckToken {
64100
readonly expireTimeMillis: number;
65101
}
66102

103+
/**
104+
* Result returned by `getToken()`.
105+
*/
106+
interface AppCheckTokenResult {
107+
/**
108+
* The token string in JWT format.
109+
*/
110+
readonly token: string;
111+
}
112+
67113
export type AppCheckComponentName = 'appCheck';
68114
declare module '@firebase/component' {
69115
interface NameServiceMapping {

packages/app-check/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"build": "rollup -c",
1717
"build:deps": "lerna run --scope @firebase/app-check --include-dependencies build",
1818
"dev": "rollup -c -w",
19-
"test": "yarn type-check && yarn test:browser",
19+
"test": "yarn lint && yarn type-check && yarn test:browser",
2020
"test:ci": "node ../../scripts/run_tests_in_ci.js",
2121
"test:browser": "karma start --single-run",
2222
"test:browser:debug": "karma start --browsers Chrome --auto-watch",

0 commit comments

Comments
 (0)