Skip to content

Commit 97d48c7

Browse files
authoredJan 21, 2025··
Add App Check token to FirebaseServerApp (#8651)
FirebaseServerApp now accepts an optional App Check token at initialization. The product SDKs will look for this token, and if it's present, the SDKs will use this value in lieu of calling getToken on App Check. This change affects the following SDKs: Auth, Cloud Functions, Data Connect, Firestore, Realtime Database, Vertex AI
1 parent 3aefcc3 commit 97d48c7

File tree

21 files changed

+262
-52
lines changed

21 files changed

+262
-52
lines changed
 

‎.changeset/kind-pets-sin.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@firebase/app': minor
3+
'firebase': minor
4+
'@firebase/data-connect': patch
5+
'@firebase/firestore': patch
6+
'@firebase/functions': patch
7+
'@firebase/database': patch
8+
'@firebase/vertexai': patch
9+
'@firebase/storage': patch
10+
'@firebase/auth': patch
11+
---
12+
13+
`FirebaseServerApp` can now be initalized with an App Check token instead of invoking the App Check
14+
`getToken` method. This should unblock the use of App Check enforced products in SSR environments
15+
where the App Check SDK cannot be initialized.

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export interface FirebaseServerApp extends FirebaseApp {
7979

8080
// @public
8181
export interface FirebaseServerAppSettings extends Omit<FirebaseAppSettings, 'name'> {
82+
appCheckToken?: string;
8283
authIdToken?: string;
8384
releaseOnDeref?: object;
8485
}
@@ -115,7 +116,7 @@ export function initializeServerApp(options: FirebaseOptions | FirebaseApp, conf
115116
export function _isFirebaseApp(obj: FirebaseApp | FirebaseOptions): obj is FirebaseApp;
116117

117118
// @internal (undocumented)
118-
export function _isFirebaseServerApp(obj: FirebaseApp | FirebaseServerApp): obj is FirebaseServerApp;
119+
export function _isFirebaseServerApp(obj: FirebaseApp | FirebaseServerApp | null | undefined): obj is FirebaseServerApp;
119120

120121
// @public
121122
export function onLog(logCallback: LogCallback | null, options?: LogOptions): void;

‎docs-devsite/app.firebaseserverappsettings.md

+17-2
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,31 @@ export interface FirebaseServerAppSettings extends Omit<FirebaseAppSettings, 'na
2323
2424
| Property | Type | Description |
2525
| --- | --- | --- |
26-
| [authIdToken](./app.firebaseserverappsettings.md#firebaseserverappsettingsauthidtoken) | string | An optional Auth ID token used to resume a signed in user session from a client runtime environment.<!-- -->Invoking <code>getAuth</code> with a <code>FirebaseServerApp</code> configured with a validated <code>authIdToken</code> causes an automatic attempt to sign in the user that the <code>authIdToken</code> represents. The token needs to have been recently minted for this operation to succeed.<!-- -->If the token fails local verification, or if the Auth service has failed to validate it when the Auth SDK is initialized, then a warning is logged to the console and the Auth SDK will not sign in a user on initialization.<!-- -->If a user is successfully signed in, then the Auth instance's <code>onAuthStateChanged</code> callback is invoked with the <code>User</code> object as per standard Auth flows. However, <code>User</code> objects created via an <code>authIdToken</code> do not have a refresh token. Attempted <code>refreshToken</code> operations fail. |
26+
| [appCheckToken](./app.firebaseserverappsettings.md#firebaseserverappsettingsappchecktoken) | string | An optional App Check token. If provided, the Firebase SDKs that use App Check will utilize this App Check token in place of requiring an instance of App Check to be initialized.<!-- -->If the token fails local verification due to expiration or parsing errors, then a console error is logged at the time of initialization of the <code>FirebaseServerApp</code> instance. |
27+
| [authIdToken](./app.firebaseserverappsettings.md#firebaseserverappsettingsauthidtoken) | string | An optional Auth ID token used to resume a signed in user session from a client runtime environment.<!-- -->Invoking <code>getAuth</code> with a <code>FirebaseServerApp</code> configured with a validated <code>authIdToken</code> causes an automatic attempt to sign in the user that the <code>authIdToken</code> represents. The token needs to have been recently minted for this operation to succeed.<!-- -->If the token fails local verification due to expiration or parsing errors, then a console error is logged at the time of initialization of the <code>FirebaseServerApp</code> instance.<!-- -->If the Auth service has failed to validate the token when the Auth SDK is initialized, then an warning is logged to the console and the Auth SDK will not sign in a user on initialization.<!-- -->If a user is successfully signed in, then the Auth instance's <code>onAuthStateChanged</code> callback is invoked with the <code>User</code> object as per standard Auth flows. However, <code>User</code> objects created via an <code>authIdToken</code> do not have a refresh token. Attempted <code>refreshToken</code> operations fail. |
2728
| [releaseOnDeref](./app.firebaseserverappsettings.md#firebaseserverappsettingsreleaseonderef) | object | An optional object. If provided, the Firebase SDK uses a <code>FinalizationRegistry</code> object to monitor the garbage collection status of the provided object. The Firebase SDK releases its reference on the <code>FirebaseServerApp</code> instance when the provided <code>releaseOnDeref</code> object is garbage collected.<!-- -->You can use this field to reduce memory management overhead for your application. If provided, an app running in a SSR pass does not need to perform <code>FirebaseServerApp</code> cleanup, so long as the reference object is deleted (by falling out of SSR scope, for instance.)<!-- -->If an object is not provided then the application must clean up the <code>FirebaseServerApp</code> instance by invoking <code>deleteApp</code>.<!-- -->If the application provides an object in this parameter, but the application is executed in a JavaScript engine that predates the support of <code>FinalizationRegistry</code> (introduced in node v14.6.0, for instance), then an error is thrown at <code>FirebaseServerApp</code> initialization. |
2829
30+
## FirebaseServerAppSettings.appCheckToken
31+
32+
An optional App Check token. If provided, the Firebase SDKs that use App Check will utilize this App Check token in place of requiring an instance of App Check to be initialized.
33+
34+
If the token fails local verification due to expiration or parsing errors, then a console error is logged at the time of initialization of the `FirebaseServerApp` instance.
35+
36+
<b>Signature:</b>
37+
38+
```typescript
39+
appCheckToken?: string;
40+
```
41+
2942
## FirebaseServerAppSettings.authIdToken
3043
3144
An optional Auth ID token used to resume a signed in user session from a client runtime environment.
3245
3346
Invoking `getAuth` with a `FirebaseServerApp` configured with a validated `authIdToken` causes an automatic attempt to sign in the user that the `authIdToken` represents. The token needs to have been recently minted for this operation to succeed.
3447
35-
If the token fails local verification, or if the Auth service has failed to validate it when the Auth SDK is initialized, then a warning is logged to the console and the Auth SDK will not sign in a user on initialization.
48+
If the token fails local verification due to expiration or parsing errors, then a console error is logged at the time of initialization of the `FirebaseServerApp` instance.
49+
50+
If the Auth service has failed to validate the token when the Auth SDK is initialized, then an warning is logged to the console and the Auth SDK will not sign in a user on initialization.
3651
3752
If a user is successfully signed in, then the Auth instance's `onAuthStateChanged` callback is invoked with the `User` object as per standard Auth flows. However, `User` objects created via an `authIdToken` do not have a refresh token. Attempted `refreshToken` operations fail.
3853

‎packages/app/src/firebaseServerApp.test.ts

+59
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ import '../test/setup';
2020
import { ComponentContainer } from '@firebase/component';
2121
import { FirebaseServerAppImpl } from './firebaseServerApp';
2222
import { FirebaseServerAppSettings } from './public-types';
23+
import { base64Encode } from '@firebase/util';
24+
25+
const BASE64_DUMMY = base64Encode('dummystrings'); // encodes to ZHVtbXlzdHJpbmdz
26+
27+
// Creates a three part dummy token with an expiration claim in the second part. The expration
28+
// time is based on the date offset provided.
29+
function createServerAppTokenWithOffset(daysOffset: number): string {
30+
const timeInSeconds = Math.trunc(
31+
new Date().setDate(new Date().getDate() + daysOffset) / 1000
32+
);
33+
const secondPart = JSON.stringify({ exp: timeInSeconds });
34+
const token =
35+
BASE64_DUMMY + '.' + base64Encode(secondPart) + '.' + BASE64_DUMMY;
36+
return token;
37+
}
2338

2439
describe('FirebaseServerApp', () => {
2540
it('has various accessors', () => {
@@ -155,4 +170,48 @@ describe('FirebaseServerApp', () => {
155170

156171
expect(JSON.stringify(app)).to.eql(undefined);
157172
});
173+
174+
it('accepts a valid authIdToken expiration', () => {
175+
const options = { apiKey: 'APIKEY' };
176+
const authIdToken = createServerAppTokenWithOffset(/*daysOffset=*/ 1);
177+
const serverAppSettings: FirebaseServerAppSettings = {
178+
automaticDataCollectionEnabled: false,
179+
releaseOnDeref: options,
180+
authIdToken
181+
};
182+
let encounteredError = false;
183+
try {
184+
new FirebaseServerAppImpl(
185+
options,
186+
serverAppSettings,
187+
'testName',
188+
new ComponentContainer('test')
189+
);
190+
} catch (e) {
191+
encounteredError = true;
192+
}
193+
expect(encounteredError).to.be.false;
194+
});
195+
196+
it('accepts a valid appCheckToken expiration', () => {
197+
const options = { apiKey: 'APIKEY' };
198+
const appCheckToken = createServerAppTokenWithOffset(/*daysOffset=*/ 1);
199+
const serverAppSettings: FirebaseServerAppSettings = {
200+
automaticDataCollectionEnabled: false,
201+
releaseOnDeref: options,
202+
appCheckToken
203+
};
204+
let encounteredError = false;
205+
try {
206+
new FirebaseServerAppImpl(
207+
options,
208+
serverAppSettings,
209+
'testName',
210+
new ComponentContainer('test')
211+
);
212+
} catch (e) {
213+
encounteredError = true;
214+
}
215+
expect(encounteredError).to.be.false;
216+
});
158217
});

‎packages/app/src/firebaseServerApp.ts

+39
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,35 @@ import { ComponentContainer } from '@firebase/component';
2626
import { FirebaseAppImpl } from './firebaseApp';
2727
import { ERROR_FACTORY, AppError } from './errors';
2828
import { name as packageName, version } from '../package.json';
29+
import { base64Decode } from '@firebase/util';
30+
31+
// Parse the token and check to see if the `exp` claim is in the future.
32+
// Reports an error to the console if the token or claim could not be parsed, or if `exp` is in
33+
// the past.
34+
function validateTokenTTL(base64Token: string, tokenName: string): void {
35+
const secondPart = base64Decode(base64Token.split('.')[1]);
36+
if (secondPart === null) {
37+
console.error(
38+
`FirebaseServerApp ${tokenName} is invalid: second part could not be parsed.`
39+
);
40+
return;
41+
}
42+
const expClaim = JSON.parse(secondPart).exp;
43+
if (expClaim === undefined) {
44+
console.error(
45+
`FirebaseServerApp ${tokenName} is invalid: expiration claim could not be parsed`
46+
);
47+
return;
48+
}
49+
const exp = JSON.parse(secondPart).exp * 1000;
50+
const now = new Date().getTime();
51+
const diff = exp - now;
52+
if (diff <= 0) {
53+
console.error(
54+
`FirebaseServerApp ${tokenName} is invalid: the token has expired.`
55+
);
56+
}
57+
}
2958

3059
export class FirebaseServerAppImpl
3160
extends FirebaseAppImpl
@@ -67,6 +96,16 @@ export class FirebaseServerAppImpl
6796
...serverConfig
6897
};
6998

99+
// Ensure that the current time is within the `authIdtoken` window of validity.
100+
if (this._serverConfig.authIdToken) {
101+
validateTokenTTL(this._serverConfig.authIdToken, 'authIdToken');
102+
}
103+
104+
// Ensure that the current time is within the `appCheckToken` window of validity.
105+
if (this._serverConfig.appCheckToken) {
106+
validateTokenTTL(this._serverConfig.appCheckToken, 'appCheckToken');
107+
}
108+
70109
this._finalizationRegistry = null;
71110
if (typeof FinalizationRegistry !== 'undefined') {
72111
this._finalizationRegistry = new FinalizationRegistry(() => {

‎packages/app/src/internal.test.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { expect } from 'chai';
1919
import { stub } from 'sinon';
2020
import '../test/setup';
2121
import { createTestComponent, TestService } from '../test/util';
22-
import { initializeApp, getApps, deleteApp } from './api';
22+
import { initializeApp, initializeServerApp, getApps, deleteApp } from './api';
2323
import { FirebaseAppImpl } from './firebaseApp';
2424
import {
2525
_addComponent,
@@ -28,9 +28,11 @@ import {
2828
_components,
2929
_clearComponents,
3030
_getProvider,
31-
_removeServiceInstance
31+
_removeServiceInstance,
32+
_isFirebaseServerApp
3233
} from './internal';
3334
import { logger } from './logger';
35+
import { isBrowser } from '@firebase/util';
3436

3537
declare module '@firebase/component' {
3638
interface NameServiceMapping {
@@ -161,4 +163,25 @@ describe('Internal API tests', () => {
161163
expect(instance1).to.not.equal(instance2);
162164
});
163165
});
166+
167+
describe('_isFirebaseServerApp', () => {
168+
it('detects a valid FirebaseServerApp', () => {
169+
if (!isBrowser()) {
170+
// FirebaseServerApp isn't supported for execution in browser environments.
171+
const app = initializeServerApp({}, {});
172+
expect(_isFirebaseServerApp(app)).to.be.true;
173+
}
174+
});
175+
it('a standard FirebaseApp returns false', () => {
176+
const app = initializeApp({});
177+
expect(_isFirebaseServerApp(app)).to.be.false;
178+
});
179+
it('a null object returns false', () => {
180+
expect(_isFirebaseServerApp(null)).to.be.false;
181+
});
182+
it('undefined returns false', () => {
183+
let app: undefined;
184+
expect(_isFirebaseServerApp(app)).to.be.false;
185+
});
186+
});
164187
});

‎packages/app/src/internal.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,11 @@ export function _isFirebaseApp(
168168
* @internal
169169
*/
170170
export function _isFirebaseServerApp(
171-
obj: FirebaseApp | FirebaseServerApp
171+
obj: FirebaseApp | FirebaseServerApp | null | undefined
172172
): obj is FirebaseServerApp {
173+
if (obj === null || obj === undefined) {
174+
return false;
175+
}
173176
return (obj as FirebaseServerApp).settings !== undefined;
174177
}
175178

‎packages/app/src/public-types.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,11 @@ export interface FirebaseServerAppSettings
185185
* causes an automatic attempt to sign in the user that the `authIdToken` represents. The token
186186
* needs to have been recently minted for this operation to succeed.
187187
*
188-
* If the token fails local verification, or if the Auth service has failed to validate it when
189-
* the Auth SDK is initialized, then a warning is logged to the console and the Auth SDK will not
190-
* sign in a user on initialization.
188+
* If the token fails local verification due to expiration or parsing errors, then a console error
189+
* is logged at the time of initialization of the `FirebaseServerApp` instance.
190+
*
191+
* If the Auth service has failed to validate the token when the Auth SDK is initialized, then an
192+
* warning is logged to the console and the Auth SDK will not sign in a user on initialization.
191193
*
192194
* If a user is successfully signed in, then the Auth instance's `onAuthStateChanged` callback
193195
* is invoked with the `User` object as per standard Auth flows. However, `User` objects
@@ -196,6 +198,15 @@ export interface FirebaseServerAppSettings
196198
*/
197199
authIdToken?: string;
198200

201+
/**
202+
* An optional App Check token. If provided, the Firebase SDKs that use App Check will utilize
203+
* this App Check token in place of requiring an instance of App Check to be initialized.
204+
*
205+
* If the token fails local verification due to expiration or parsing errors, then a console error
206+
* is logged at the time of initialization of the `FirebaseServerApp` instance.
207+
*/
208+
appCheckToken?: string;
209+
199210
/**
200211
* An optional object. If provided, the Firebase SDK uses a `FinalizationRegistry`
201212
* object to monitor the garbage collection status of the provided object. The

‎packages/auth/src/core/auth/auth_impl.ts

+3
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,9 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
845845
}
846846

847847
async _getAppCheckToken(): Promise<string | undefined> {
848+
if (_isFirebaseServerApp(this.app) && this.app.settings.appCheckToken) {
849+
return this.app.settings.appCheckToken;
850+
}
848851
const appCheckTokenResult = await this.appCheckServiceProvider
849852
.getImmediate({ optional: true })
850853
?.getToken();

‎packages/auth/test/integration/flows/firebaseserverapp.test.ts

-31
Original file line numberDiff line numberDiff line change
@@ -166,37 +166,6 @@ describe('Integration test: Auth FirebaseServerApp tests', () => {
166166
await deleteApp(serverApp);
167167
});
168168

169-
it('invalid token does not sign in user', async () => {
170-
if (isBrowser()) {
171-
return;
172-
}
173-
const authIdToken = '{ invalid token }';
174-
const firebaseServerAppSettings = { authIdToken };
175-
176-
const serverApp = initializeServerApp(
177-
getAppConfig(),
178-
firebaseServerAppSettings
179-
);
180-
const serverAppAuth = getTestInstanceForServerApp(serverApp);
181-
expect(serverAppAuth.currentUser).to.be.null;
182-
183-
let numberServerLogins = 0;
184-
onAuthStateChanged(serverAppAuth, serverAuthUser => {
185-
if (serverAuthUser) {
186-
numberServerLogins++;
187-
}
188-
});
189-
190-
await new Promise(resolve => {
191-
setTimeout(resolve, signInWaitDuration);
192-
});
193-
194-
expect(numberServerLogins).to.equal(0);
195-
expect(serverAppAuth.currentUser).to.be.null;
196-
197-
await deleteApp(serverApp);
198-
});
199-
200169
it('signs in with email credentials user', async () => {
201170
if (isBrowser()) {
202171
return;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export class DataConnect {
151151
}
152152
if (this._appCheckProvider) {
153153
this._appCheckTokenProvider = new AppCheckTokenProvider(
154-
this.app.name,
154+
this.app,
155155
this._appCheckProvider
156156
);
157157
}

‎packages/data-connect/src/core/AppCheckTokenProvider.ts

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

18+
import { FirebaseApp, _isFirebaseServerApp } from '@firebase/app';
1819
import {
1920
AppCheckInternalComponentName,
2021
AppCheckTokenListener,
@@ -29,10 +30,14 @@ import { Provider } from '@firebase/component';
2930
*/
3031
export class AppCheckTokenProvider {
3132
private appCheck?: FirebaseAppCheckInternal;
33+
private serverAppAppCheckToken?: string;
3234
constructor(
33-
private appName_: string,
35+
app: FirebaseApp,
3436
private appCheckProvider?: Provider<AppCheckInternalComponentName>
3537
) {
38+
if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
39+
this.serverAppAppCheckToken = app.settings.appCheckToken;
40+
}
3641
this.appCheck = appCheckProvider?.getImmediate({ optional: true });
3742
if (!this.appCheck) {
3843
void appCheckProvider
@@ -42,7 +47,11 @@ export class AppCheckTokenProvider {
4247
}
4348
}
4449

45-
getToken(forceRefresh?: boolean): Promise<AppCheckTokenResult> {
50+
getToken(): Promise<AppCheckTokenResult> {
51+
if (this.serverAppAppCheckToken) {
52+
return Promise.resolve({ token: this.serverAppAppCheckToken });
53+
}
54+
4655
if (!this.appCheck) {
4756
return new Promise<AppCheckTokenResult>((resolve, reject) => {
4857
// Support delayed initialization of FirebaseAppCheck. This allows our
@@ -51,14 +60,14 @@ export class AppCheckTokenProvider {
5160
// becomes available before the timoeout below expires.
5261
setTimeout(() => {
5362
if (this.appCheck) {
54-
this.getToken(forceRefresh).then(resolve, reject);
63+
this.getToken().then(resolve, reject);
5564
} else {
5665
resolve(null);
5766
}
5867
}, 0);
5968
});
6069
}
61-
return this.appCheck.getToken(forceRefresh);
70+
return this.appCheck.getToken();
6271
}
6372

6473
addTokenChangeListener(listener: AppCheckTokenListener): void {

‎packages/database/src/api/Database.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export function repoManagerDatabaseFromApp(
164164
repoInfo,
165165
app,
166166
authTokenProvider,
167-
new AppCheckTokenProvider(app.name, appCheckProvider)
167+
new AppCheckTokenProvider(app, appCheckProvider)
168168
);
169169
return new Database(repo, app);
170170
}

‎packages/database/src/core/AppCheckTokenProvider.ts

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

18+
import { FirebaseApp, _isFirebaseServerApp } from '@firebase/app'; // eslint-disable-line import/no-extraneous-dependencies
1819
import {
1920
AppCheckInternalComponentName,
2021
AppCheckTokenListener,
@@ -30,17 +31,31 @@ import { warn } from './util/util';
3031
*/
3132
export class AppCheckTokenProvider {
3233
private appCheck?: FirebaseAppCheckInternal;
34+
private serverAppAppCheckToken?: string;
35+
private appName: string;
3336
constructor(
34-
private appName_: string,
37+
app: FirebaseApp,
3538
private appCheckProvider?: Provider<AppCheckInternalComponentName>
3639
) {
40+
this.appName = app.name;
41+
if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
42+
this.serverAppAppCheckToken = app.settings.appCheckToken;
43+
}
3744
this.appCheck = appCheckProvider?.getImmediate({ optional: true });
3845
if (!this.appCheck) {
3946
appCheckProvider?.get().then(appCheck => (this.appCheck = appCheck));
4047
}
4148
}
4249

4350
getToken(forceRefresh?: boolean): Promise<AppCheckTokenResult> {
51+
if (this.serverAppAppCheckToken) {
52+
if (forceRefresh) {
53+
throw new Error(
54+
'Attempted reuse of `FirebaseServerApp.appCheckToken` after previous usage failed.'
55+
);
56+
}
57+
return Promise.resolve({ token: this.serverAppAppCheckToken });
58+
}
4459
if (!this.appCheck) {
4560
return new Promise<AppCheckTokenResult>((resolve, reject) => {
4661
// Support delayed initialization of FirebaseAppCheck. This allows our
@@ -67,7 +82,7 @@ export class AppCheckTokenProvider {
6782

6883
notifyForInvalidToken(): void {
6984
warn(
70-
`Provided AppCheck credentials for the app named "${this.appName_}" ` +
85+
`Provided AppCheck credentials for the app named "${this.appName}" ` +
7186
'are invalid. This usually indicates your app was not initialized correctly.'
7287
);
7388
}

‎packages/firestore/lite/register.ts

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export function registerFirestore(): void {
4949
container.getProvider('auth-internal')
5050
),
5151
new LiteAppCheckTokenProvider(
52+
app,
5253
container.getProvider('app-check-internal')
5354
),
5455
databaseIdFromApp(app, databaseId),

‎packages/firestore/src/api/credentials.ts

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

18+
import { FirebaseApp, _isFirebaseServerApp } from '@firebase/app';
1819
import {
1920
AppCheckInternalComponentName,
2021
AppCheckTokenListener,
@@ -495,10 +496,16 @@ export class FirebaseAppCheckTokenProvider
495496
private forceRefresh = false;
496497
private appCheck: FirebaseAppCheckInternal | null = null;
497498
private latestAppCheckToken: string | null = null;
499+
private serverAppAppCheckToken: string | null = null;
498500

499501
constructor(
502+
app: FirebaseApp,
500503
private appCheckProvider: Provider<AppCheckInternalComponentName>
501-
) {}
504+
) {
505+
if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
506+
this.serverAppAppCheckToken = app.settings.appCheckToken;
507+
}
508+
}
502509

503510
start(
504511
asyncQueue: AsyncQueue,
@@ -562,6 +569,9 @@ export class FirebaseAppCheckTokenProvider
562569
}
563570

564571
getToken(): Promise<Token | null> {
572+
if (this.serverAppAppCheckToken) {
573+
return Promise.resolve(new AppCheckToken(this.serverAppAppCheckToken));
574+
}
565575
debugAssert(
566576
this.tokenListener != null,
567577
'FirebaseAppCheckTokenProvider not started.'
@@ -622,16 +632,25 @@ export class EmptyAppCheckTokenProvider implements CredentialsProvider<string> {
622632
/** AppCheck token provider for the Lite SDK. */
623633
export class LiteAppCheckTokenProvider implements CredentialsProvider<string> {
624634
private appCheck: FirebaseAppCheckInternal | null = null;
635+
private serverAppAppCheckToken: string | null = null;
625636

626637
constructor(
638+
app: FirebaseApp,
627639
private appCheckProvider: Provider<AppCheckInternalComponentName>
628640
) {
641+
if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
642+
this.serverAppAppCheckToken = app.settings.appCheckToken;
643+
}
629644
appCheckProvider.onInit(appCheck => {
630645
this.appCheck = appCheck;
631646
});
632647
}
633648

634649
getToken(): Promise<Token | null> {
650+
if (this.serverAppAppCheckToken) {
651+
return Promise.resolve(new AppCheckToken(this.serverAppAppCheckToken));
652+
}
653+
635654
if (!this.appCheck) {
636655
return Promise.resolve(null);
637656
}

‎packages/firestore/src/register.ts

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export function registerFirestore(
4747
container.getProvider('auth-internal')
4848
),
4949
new FirebaseAppCheckTokenProvider(
50+
app,
5051
container.getProvider('app-check-internal')
5152
),
5253
databaseIdFromApp(app, databaseId),

‎packages/functions/src/context.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import { Provider } from '@firebase/component';
19+
import { _isFirebaseServerApp, FirebaseApp } from '@firebase/app';
1920
import {
2021
AppCheckInternalComponentName,
2122
FirebaseAppCheckInternal
@@ -47,11 +48,16 @@ export class ContextProvider {
4748
private auth: FirebaseAuthInternal | null = null;
4849
private messaging: MessagingInternal | null = null;
4950
private appCheck: FirebaseAppCheckInternal | null = null;
51+
private serverAppAppCheckToken: string | null = null;
5052
constructor(
53+
readonly app: FirebaseApp,
5154
authProvider: Provider<FirebaseAuthInternalName>,
5255
messagingProvider: Provider<MessagingInternalComponentName>,
5356
appCheckProvider: Provider<AppCheckInternalComponentName>
5457
) {
58+
if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
59+
this.serverAppAppCheckToken = app.settings.appCheckToken;
60+
}
5561
this.auth = authProvider.getImmediate({ optional: true });
5662
this.messaging = messagingProvider.getImmediate({
5763
optional: true
@@ -76,7 +82,7 @@ export class ContextProvider {
7682
}
7783

7884
if (!this.appCheck) {
79-
appCheckProvider.get().then(
85+
appCheckProvider?.get().then(
8086
appCheck => (this.appCheck = appCheck),
8187
() => {
8288
/* get() never rejects */
@@ -122,6 +128,9 @@ export class ContextProvider {
122128
async getAppCheckToken(
123129
limitedUseAppCheckTokens?: boolean
124130
): Promise<string | null> {
131+
if (this.serverAppAppCheckToken) {
132+
return this.serverAppAppCheckToken;
133+
}
125134
if (this.appCheck) {
126135
const result = limitedUseAppCheckTokens
127136
? await this.appCheck.getLimitedUseToken()

‎packages/functions/src/service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export class FunctionsService implements _FirebaseService {
112112
readonly fetchImpl: typeof fetch = (...args) => fetch(...args)
113113
) {
114114
this.contextProvider = new ContextProvider(
115+
app,
115116
authProvider,
116117
messagingProvider,
117118
appCheckProvider

‎packages/storage/src/service.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ import { Provider } from '@firebase/component';
2424
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
2525
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
2626
// eslint-disable-next-line import/no-extraneous-dependencies
27-
import { FirebaseApp, FirebaseOptions } from '@firebase/app';
27+
import {
28+
FirebaseApp,
29+
FirebaseOptions,
30+
_isFirebaseServerApp
31+
} from '@firebase/app';
2832
import {
2933
CONFIG_STORAGE_BUCKET_KEY,
3034
DEFAULT_HOST,
@@ -262,6 +266,9 @@ export class FirebaseStorageImpl implements FirebaseStorage {
262266
}
263267

264268
async _getAppCheckToken(): Promise<string | null> {
269+
if (_isFirebaseServerApp(this.app) && this.app.settings.appCheckToken) {
270+
return this.app.settings.appCheckToken;
271+
}
265272
const appCheck = this._appCheckProvider.getImmediate({ optional: true });
266273
if (appCheck) {
267274
const result = await appCheck.getToken();

‎packages/vertexai/src/models/generative-model.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
import { VertexAI } from '../public-types';
4747
import { ApiSettings } from '../types/internal';
4848
import { VertexAIService } from '../service';
49+
import { _isFirebaseServerApp } from '@firebase/app';
4950

5051
/**
5152
* Class for generative model APIs.
@@ -82,7 +83,16 @@ export class GenerativeModel {
8283
project: vertexAI.app.options.projectId,
8384
location: vertexAI.location
8485
};
85-
if ((vertexAI as VertexAIService).appCheck) {
86+
87+
if (
88+
_isFirebaseServerApp(vertexAI.app) &&
89+
vertexAI.app.settings.appCheckToken
90+
) {
91+
const token = vertexAI.app.settings.appCheckToken;
92+
this._apiSettings.getAppCheckToken = () => {
93+
return Promise.resolve({ token });
94+
};
95+
} else if ((vertexAI as VertexAIService).appCheck) {
8696
this._apiSettings.getAppCheckToken = () =>
8797
(vertexAI as VertexAIService).appCheck!.getToken();
8898
}

0 commit comments

Comments
 (0)
Please sign in to comment.