Skip to content

Commit 93e9d8b

Browse files
committed
Merge branch 'dataconnect' of github.com:Firebase/firebase-js-sdk into dataconnect
2 parents 3d880c8 + 44c0f63 commit 93e9d8b

File tree

7 files changed

+112
-4
lines changed

7 files changed

+112
-4
lines changed

packages/data-connect/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
# @firebase/data-connect
1+
## Unreleased
2+
* Added app check support # @firebase/data-connect
3+
24
## 0.0.3
35
* Updated reporting to use @firebase/data-connect instead of @firebase/connect.
46
* Added functionality to retry queries and mutations if the server responds with UNAUTHENTICATED.
57
* Moved `validateArgs` to core SDK.
68
* Updated errors to only show relevant details to the user.
79
* Added ability to track whether user is calling core sdk or generated sdk.
10+

packages/data-connect/src/api/DataConnect.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ import { DataConnectTransport, TransportClass } from '../network';
3535
import { RESTTransport } from '../network/transport/rest';
3636

3737
import { MutationManager } from './Mutation';
38+
import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider';
39+
import {
40+
AppCheckInternalComponentName,
41+
FirebaseAppCheckInternal
42+
} from '@firebase/app-check-interop-types';
3843

3944
/**
4045
* Connector Config for calling Data Connect backend.
@@ -90,11 +95,13 @@ export class DataConnect {
9095
private _transportOptions?: TransportOptions;
9196
private _authTokenProvider?: AuthTokenProvider;
9297
_isUsingGeneratedSdk: boolean = false;
98+
private _appCheckTokenProvider?: AppCheckTokenProvider;
9399
constructor(
94100
public readonly app: FirebaseApp,
95101
// TODO(mtewani): Replace with _dataConnectOptions in the future
96102
private readonly dataConnectOptions: DataConnectOptions,
97-
private readonly _authProvider: Provider<FirebaseAuthInternalName>
103+
private readonly _authProvider: Provider<FirebaseAuthInternalName>,
104+
private readonly _appCheckProvider: Provider<AppCheckInternalComponentName>
98105
) {
99106
if (typeof process !== 'undefined' && process.env) {
100107
const host = process.env[FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR];
@@ -144,12 +151,19 @@ export class DataConnect {
144151
this._authProvider
145152
);
146153
}
154+
if (this._appCheckProvider) {
155+
this._appCheckTokenProvider = new AppCheckTokenProvider(
156+
this.app.name,
157+
this._appCheckProvider
158+
);
159+
}
147160

148161
this.initialized = true;
149162
this._transport = new this._transportClass(
150163
this.dataConnectOptions,
151164
this.app.options.apiKey,
152165
this._authTokenProvider,
166+
this._appCheckTokenProvider,
153167
undefined,
154168
this._isUsingGeneratedSdk
155169
);
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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 {
19+
AppCheckInternalComponentName,
20+
AppCheckTokenListener,
21+
AppCheckTokenResult,
22+
FirebaseAppCheckInternal
23+
} from '@firebase/app-check-interop-types';
24+
import { Provider } from '@firebase/component';
25+
26+
/**
27+
* Abstraction around AppCheck's token fetching capabilities.
28+
*/
29+
export class AppCheckTokenProvider {
30+
private appCheck?: FirebaseAppCheckInternal;
31+
constructor(
32+
private appName_: string,
33+
private appCheckProvider?: Provider<AppCheckInternalComponentName>
34+
) {
35+
this.appCheck = appCheckProvider?.getImmediate({ optional: true });
36+
if (!this.appCheck) {
37+
appCheckProvider?.get().then(appCheck => (this.appCheck = appCheck));
38+
}
39+
}
40+
41+
getToken(forceRefresh?: boolean): Promise<AppCheckTokenResult> {
42+
if (!this.appCheck) {
43+
return new Promise<AppCheckTokenResult>((resolve, reject) => {
44+
// Support delayed initialization of FirebaseAppCheck. This allows our
45+
// customers to initialize the RTDB SDK before initializing Firebase
46+
// AppCheck and ensures that all requests are authenticated if a token
47+
// becomes available before the timoeout below expires.
48+
setTimeout(() => {
49+
if (this.appCheck) {
50+
this.getToken(forceRefresh).then(resolve, reject);
51+
} else {
52+
resolve(null);
53+
}
54+
}, 0);
55+
});
56+
}
57+
return this.appCheck.getToken(forceRefresh);
58+
}
59+
60+
addTokenChangeListener(listener: AppCheckTokenListener) {
61+
this.appCheckProvider
62+
?.get()
63+
.then(appCheck => appCheck.addTokenListener(listener));
64+
}
65+
66+
// Not currently used at the moment. Will update if needed.
67+
// notifyForInvalidToken(): void {
68+
// warn(
69+
// `Provided AppCheck credentials for the app named "${this.appName_}" ` +
70+
// 'are invalid. This usually indicates your app was not initialized correctly.'
71+
// );
72+
// }
73+
}

packages/data-connect/src/network/fetch.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export function dcFetch<T, U>(
3535
body: U,
3636
{ signal }: AbortController,
3737
accessToken: string | null,
38+
appCheckToken: string | null,
3839
_isUsingGen: boolean
3940
): Promise<{ data: T; errors: Error[] }> {
4041
if (!connectFetch) {
@@ -47,6 +48,9 @@ export function dcFetch<T, U>(
4748
if (accessToken) {
4849
headers['X-Firebase-Auth-Token'] = accessToken;
4950
}
51+
if (appCheckToken) {
52+
headers['X-Firebase-AppCheck'] = appCheckToken;
53+
}
5054
const bodyStr = JSON.stringify(body);
5155
logDebug(`Making request out to ${url} with body: ${bodyStr}`);
5256

packages/data-connect/src/network/transport/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import { DataConnectOptions, TransportOptions } from '../../api/DataConnect';
19+
import { AppCheckTokenProvider } from '../../core/AppCheckTokenProvider';
1920
import { AuthTokenProvider } from '../../core/FirebaseAuthProvider';
2021

2122
// Change this to only specify specific args.
@@ -52,6 +53,7 @@ export type TransportClass = new (
5253
options: DataConnectOptions,
5354
apiKey?: string,
5455
authProvider?: AuthTokenProvider,
56+
appCheckProvider?: AppCheckTokenProvider,
5557
transportOptions?: TransportOptions,
5658
_isUsingGen?: boolean
5759
) => DataConnectTransport;

packages/data-connect/src/network/transport/rest.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { addToken, urlBuilder } from '../../util/url';
2323
import { dcFetch } from '../fetch';
2424

2525
import { DataConnectTransport } from '.';
26+
import { AppCheckTokenProvider } from '../../core/AppCheckTokenProvider';
2627

2728
export class RESTTransport implements DataConnectTransport {
2829
private _host = '';
@@ -33,14 +34,16 @@ export class RESTTransport implements DataConnectTransport {
3334
private _project = 'p';
3435
private _serviceName: string;
3536
private _accessToken: string | null = null;
37+
private _appCheckToken: string | null = null;
3638
private _authInitialized = false;
3739
private _lastToken: string | null = null;
3840
constructor(
3941
options: DataConnectOptions,
4042
private apiKey?: string | undefined,
4143
private authProvider?: AuthTokenProvider | undefined,
44+
private appCheckProvider?: AppCheckTokenProvider | undefined,
4245
transportOptions?: TransportOptions | undefined,
43-
private _isUsingGen = false
46+
private _isUsingGen = false,
4447
) {
4548
if (transportOptions) {
4649
if (typeof transportOptions.port === 'number') {
@@ -70,6 +73,11 @@ export class RESTTransport implements DataConnectTransport {
7073
logDebug(`New Token Available: ${token}`);
7174
this._accessToken = token;
7275
});
76+
this.appCheckProvider?.addTokenChangeListener(result => {
77+
const { token } = result;
78+
logDebug(`New App Check Token Available: ${token}`);
79+
this._appCheckToken = token;
80+
});
7381
}
7482
get endpointUrl(): string {
7583
return urlBuilder(
@@ -168,6 +176,7 @@ export class RESTTransport implements DataConnectTransport {
168176
} as unknown as U, // TODO(mtewani): This is a patch, fix this.
169177
abortController,
170178
this._accessToken,
179+
this._appCheckToken,
171180
this._isUsingGen
172181
)
173182
);
@@ -195,6 +204,7 @@ export class RESTTransport implements DataConnectTransport {
195204
} as unknown as U,
196205
abortController,
197206
this._accessToken,
207+
this._appCheckToken,
198208
this._isUsingGen
199209
);
200210
});

packages/data-connect/src/register.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export function registerDataConnect(variant?: string): void {
3636
(container, { instanceIdentifier: settings, options }) => {
3737
const app = container.getProvider('app').getImmediate()!;
3838
const authProvider = container.getProvider('auth-internal');
39+
const appCheckProvider = container.getProvider('app-check-internal');
3940
let newOpts = options as ConnectorConfig;
4041
if (settings) {
4142
newOpts = JSON.parse(settings);
@@ -49,7 +50,8 @@ export function registerDataConnect(variant?: string): void {
4950
return new DataConnect(
5051
app,
5152
{ ...newOpts, projectId: app.options.projectId! },
52-
authProvider
53+
authProvider,
54+
appCheckProvider
5355
);
5456
},
5557
ComponentType.PUBLIC

0 commit comments

Comments
 (0)