From 500168c993b37c7ef812ae6185d2d74d07e7a5cc Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Tue, 29 Oct 2024 17:31:50 -0400 Subject: [PATCH 01/13] Initial Impl --- common/api-review/app.api.md | 5 ++++ packages/app/src/errors.ts | 7 +++-- packages/app/src/firebaseServerApp.ts | 33 +++++++++++++++++++-- packages/app/src/public-types.ts | 23 ++++++++++++++ packages/installations/src/api/get-id.ts | 9 ++++++ packages/installations/src/api/get-token.ts | 8 +++++ packages/installations/src/util/errors.ts | 7 +++-- 7 files changed, 86 insertions(+), 6 deletions(-) diff --git a/common/api-review/app.api.md b/common/api-review/app.api.md index bdfb2a681f1..a3422c5174d 100644 --- a/common/api-review/app.api.md +++ b/common/api-review/app.api.md @@ -73,6 +73,10 @@ export interface FirebaseOptions { // @public export interface FirebaseServerApp extends FirebaseApp { + // (undocumented) + installationsAuthToken?: string; + // (undocumented) + installationsId?: string; name: string; readonly settings: FirebaseServerAppSettings; } @@ -80,6 +84,7 @@ export interface FirebaseServerApp extends FirebaseApp { // @public export interface FirebaseServerAppSettings extends Omit { authIdToken?: string; + installationsAuthToken?: string; releaseOnDeref?: object; } diff --git a/packages/app/src/errors.ts b/packages/app/src/errors.ts index 0149ef3dcb1..d3fb2add33d 100644 --- a/packages/app/src/errors.ts +++ b/packages/app/src/errors.ts @@ -31,7 +31,8 @@ export const enum AppError { IDB_WRITE = 'idb-set', IDB_DELETE = 'idb-delete', FINALIZATION_REGISTRY_NOT_SUPPORTED = 'finalization-registry-not-supported', - INVALID_SERVER_APP_ENVIRONMENT = 'invalid-server-app-environment' + INVALID_SERVER_APP_ENVIRONMENT = 'invalid-server-app-environment', + INVALID_SERVER_APP_INSTALLATIONS_AUTH_TOKEN = 'invalid-server-installations-auth-token' } const ERRORS: ErrorMap = { @@ -61,7 +62,9 @@ const ERRORS: ErrorMap = { [AppError.FINALIZATION_REGISTRY_NOT_SUPPORTED]: 'FirebaseServerApp deleteOnDeref field defined but the JS runtime does not support FinalizationRegistry.', [AppError.INVALID_SERVER_APP_ENVIRONMENT]: - 'FirebaseServerApp is not for use in browser environments.' + 'FirebaseServerApp is not for use in browser environments.', + [AppError.INVALID_SERVER_APP_INSTALLATIONS_AUTH_TOKEN]: + 'FirebaseServerApp could not initialize due to an invalid Installations auth token' }; interface ErrorParams { diff --git a/packages/app/src/firebaseServerApp.ts b/packages/app/src/firebaseServerApp.ts index 0c41d4cd607..435c8b823bd 100644 --- a/packages/app/src/firebaseServerApp.ts +++ b/packages/app/src/firebaseServerApp.ts @@ -26,14 +26,15 @@ import { ComponentContainer } from '@firebase/component'; import { FirebaseAppImpl } from './firebaseApp'; import { ERROR_FACTORY, AppError } from './errors'; import { name as packageName, version } from '../package.json'; +import { base64Decode } from '@firebase/util'; export class FirebaseServerAppImpl extends FirebaseAppImpl - implements FirebaseServerApp -{ + implements FirebaseServerApp { private readonly _serverConfig: FirebaseServerAppSettings; private _finalizationRegistry: FinalizationRegistry | null; private _refCount: number; + private _installationsId: string | null; constructor( options: FirebaseOptions | FirebaseAppImpl, @@ -67,6 +68,20 @@ export class FirebaseServerAppImpl ...serverConfig }; + // Parse the installationAuthToken if provided. + if (this._serverConfig.installationsAuthToken !== undefined) { + const thirdPart = this._serverConfig.installationsAuthToken.split(".")[1].split(".")[0]; + const decodedToken = base64Decode(thirdPart); + const tokenJSON = JSON.parse(decodedToken ? decodedToken : ""); + if (!decodedToken || !tokenJSON || tokenJSON.fid === undefined) { + throw ERROR_FACTORY.create(AppError.INVALID_SERVER_APP_INSTALLATIONS_AUTH_TOKEN); + } else { + this._installationsId = tokenJSON.fid; + } + } else { + this._installationsId = null; + } + this._finalizationRegistry = null; if (typeof FinalizationRegistry !== 'undefined') { this._finalizationRegistry = new FinalizationRegistry(() => { @@ -125,6 +140,20 @@ export class FirebaseServerAppImpl return this._serverConfig; } + get installationsAuthToken(): string | null { + this.checkDestroyed(); + if (this._serverConfig.installationsAuthToken !== undefined) { + return this._serverConfig.installationsAuthToken; + } else { + return null; + } + } + + get installationsId(): string | null { + this.checkDestroyed(); + return this._installationsId; + } + /** * This function will throw an Error if the App has already been deleted - * use before performing API actions on the App. diff --git a/packages/app/src/public-types.ts b/packages/app/src/public-types.ts index ff25de93a46..9f0cfa9795e 100644 --- a/packages/app/src/public-types.ts +++ b/packages/app/src/public-types.ts @@ -100,6 +100,9 @@ export interface FirebaseServerApp extends FirebaseApp { * ``` */ readonly settings: FirebaseServerAppSettings; + + readonly installationsId : string | null; + readonly installationsAuthToken : string | null; } /** @@ -196,6 +199,26 @@ export interface FirebaseServerAppSettings */ authIdToken?: string; + /** + * An optional Installations Auth token which allows the use of Remote Config SDK in + * SSR enviornments. + * + * If provided, the `FirebaseServerApp` will attempt to parse the Installations id + * from the token. + * + * If the token is deemed to be malformed then an error will be + * thrown during the invocation of `initializeServerApp`. + * + * If the the Installations Id and the provided `installationsAuthToken` are successfully parsed, + * then they will be used by the Installations implementation when `getToken` and `getId` are + * invoked. + * + * Attempting to use Remote Config without providing an `installationsAuthToken` here will cause + * Installations to throw errors when Remote Config attempts to query the Installations id and + * authToken. + */ + installationsAuthToken?: string; + /** * An optional object. If provided, the Firebase SDK uses a `FinalizationRegistry` * object to monitor the garbage collection status of the provided object. The diff --git a/packages/installations/src/api/get-id.ts b/packages/installations/src/api/get-id.ts index 589e8b49550..12e8df9e999 100644 --- a/packages/installations/src/api/get-id.ts +++ b/packages/installations/src/api/get-id.ts @@ -19,6 +19,8 @@ import { getInstallationEntry } from '../helpers/get-installation-entry'; import { refreshAuthToken } from '../helpers/refresh-auth-token'; import { FirebaseInstallationsImpl } from '../interfaces/installation-impl'; import { Installations } from '../interfaces/public-types'; +import { _isFirebaseServerApp } from '@firebase/app'; +import { ERROR_FACTORY, ErrorCode } from '../util/errors'; /** * Creates a Firebase Installation if there isn't one for the app and @@ -28,6 +30,13 @@ import { Installations } from '../interfaces/public-types'; * @public */ export async function getId(installations: Installations): Promise { + if(_isFirebaseServerApp(installations.app)) { + if(!installations.app.installationsId) { + throw ERROR_FACTORY.create(ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN); + } + return installations.app.installationsId; + } + const installationsImpl = installations as FirebaseInstallationsImpl; const { installationEntry, registrationPromise } = await getInstallationEntry( installationsImpl diff --git a/packages/installations/src/api/get-token.ts b/packages/installations/src/api/get-token.ts index 10e009e4a3a..edc67c5e76a 100644 --- a/packages/installations/src/api/get-token.ts +++ b/packages/installations/src/api/get-token.ts @@ -19,6 +19,8 @@ import { getInstallationEntry } from '../helpers/get-installation-entry'; import { refreshAuthToken } from '../helpers/refresh-auth-token'; import { FirebaseInstallationsImpl } from '../interfaces/installation-impl'; import { Installations } from '../interfaces/public-types'; +import { _isFirebaseServerApp } from '@firebase/app'; +import { ERROR_FACTORY, ErrorCode } from '../util/errors'; /** * Returns a Firebase Installations auth token, identifying the current @@ -32,6 +34,12 @@ export async function getToken( installations: Installations, forceRefresh = false ): Promise { + if(_isFirebaseServerApp(installations.app)) { + if(installations.app.installationsAuthToken === undefined) { + throw ERROR_FACTORY.create(ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN); + } + return installations.app.installationsAuthToken; + } const installationsImpl = installations as FirebaseInstallationsImpl; await completeInstallationRegistration(installationsImpl); diff --git a/packages/installations/src/util/errors.ts b/packages/installations/src/util/errors.ts index 1a82753c3d2..fc465776eb1 100644 --- a/packages/installations/src/util/errors.ts +++ b/packages/installations/src/util/errors.ts @@ -24,7 +24,8 @@ export const enum ErrorCode { INSTALLATION_NOT_FOUND = 'installation-not-found', REQUEST_FAILED = 'request-failed', APP_OFFLINE = 'app-offline', - DELETE_PENDING_REGISTRATION = 'delete-pending-registration' + DELETE_PENDING_REGISTRATION = 'delete-pending-registration', + SERVER_APP_MISSING_AUTH_TOKEN = 'server-app-missing-auth-token' } const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { @@ -36,7 +37,9 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { '{$requestName} request failed with error "{$serverCode} {$serverStatus}: {$serverMessage}"', [ErrorCode.APP_OFFLINE]: 'Could not process request. Application offline.', [ErrorCode.DELETE_PENDING_REGISTRATION]: - "Can't delete installation while there is a pending registration request." + "Can't delete installation while there is a pending registration request.", + [ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN]: + "The instance of FirebaseServerApp was not initialized with a valid installationsAuthToken" }; interface ErrorParams { From 55b938b3960fbd170545bd66dc2de702c9d682be Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Wed, 30 Oct 2024 09:10:38 -0400 Subject: [PATCH 02/13] format --- common/api-review/app.api.md | 4 ++-- packages/app/src/public-types.ts | 10 +++++----- packages/installations/src/api/get-id.ts | 6 +++--- packages/installations/src/api/get-token.ts | 6 +++--- packages/installations/src/util/errors.ts | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/common/api-review/app.api.md b/common/api-review/app.api.md index a3422c5174d..864a9ff56e6 100644 --- a/common/api-review/app.api.md +++ b/common/api-review/app.api.md @@ -74,9 +74,9 @@ export interface FirebaseOptions { // @public export interface FirebaseServerApp extends FirebaseApp { // (undocumented) - installationsAuthToken?: string; + readonly installationsAuthToken: string | null; // (undocumented) - installationsId?: string; + readonly installationsId: string | null; name: string; readonly settings: FirebaseServerAppSettings; } diff --git a/packages/app/src/public-types.ts b/packages/app/src/public-types.ts index 9f0cfa9795e..26e4505443c 100644 --- a/packages/app/src/public-types.ts +++ b/packages/app/src/public-types.ts @@ -101,8 +101,8 @@ export interface FirebaseServerApp extends FirebaseApp { */ readonly settings: FirebaseServerAppSettings; - readonly installationsId : string | null; - readonly installationsAuthToken : string | null; + readonly installationsId: string | null; + readonly installationsAuthToken: string | null; } /** @@ -205,14 +205,14 @@ export interface FirebaseServerAppSettings * * If provided, the `FirebaseServerApp` will attempt to parse the Installations id * from the token. - * + * * If the token is deemed to be malformed then an error will be * thrown during the invocation of `initializeServerApp`. - * + * * If the the Installations Id and the provided `installationsAuthToken` are successfully parsed, * then they will be used by the Installations implementation when `getToken` and `getId` are * invoked. - * + * * Attempting to use Remote Config without providing an `installationsAuthToken` here will cause * Installations to throw errors when Remote Config attempts to query the Installations id and * authToken. diff --git a/packages/installations/src/api/get-id.ts b/packages/installations/src/api/get-id.ts index 12e8df9e999..ae25099b032 100644 --- a/packages/installations/src/api/get-id.ts +++ b/packages/installations/src/api/get-id.ts @@ -30,10 +30,10 @@ import { ERROR_FACTORY, ErrorCode } from '../util/errors'; * @public */ export async function getId(installations: Installations): Promise { - if(_isFirebaseServerApp(installations.app)) { - if(!installations.app.installationsId) { + if (_isFirebaseServerApp(installations.app)) { + if (!installations.app.installationsId) { throw ERROR_FACTORY.create(ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN); - } + } return installations.app.installationsId; } diff --git a/packages/installations/src/api/get-token.ts b/packages/installations/src/api/get-token.ts index edc67c5e76a..354919c5014 100644 --- a/packages/installations/src/api/get-token.ts +++ b/packages/installations/src/api/get-token.ts @@ -34,10 +34,10 @@ export async function getToken( installations: Installations, forceRefresh = false ): Promise { - if(_isFirebaseServerApp(installations.app)) { - if(installations.app.installationsAuthToken === undefined) { + if (_isFirebaseServerApp(installations.app)) { + if (installations.app.installationsAuthToken === undefined) { throw ERROR_FACTORY.create(ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN); - } + } return installations.app.installationsAuthToken; } const installationsImpl = installations as FirebaseInstallationsImpl; diff --git a/packages/installations/src/util/errors.ts b/packages/installations/src/util/errors.ts index fc465776eb1..88f40f26e9e 100644 --- a/packages/installations/src/util/errors.ts +++ b/packages/installations/src/util/errors.ts @@ -39,7 +39,7 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { [ErrorCode.DELETE_PENDING_REGISTRATION]: "Can't delete installation while there is a pending registration request.", [ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN]: - "The instance of FirebaseServerApp was not initialized with a valid installationsAuthToken" + 'The instance of FirebaseServerApp was not initialized with a valid installationsAuthToken' }; interface ErrorParams { From 147771a33db4c19da4c4a8148fecbc9dc53fb56a Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Wed, 30 Oct 2024 09:11:24 -0400 Subject: [PATCH 03/13] rework parsing logic at server app init --- packages/app/src/firebaseServerApp.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/app/src/firebaseServerApp.ts b/packages/app/src/firebaseServerApp.ts index 435c8b823bd..ad55a10bc04 100644 --- a/packages/app/src/firebaseServerApp.ts +++ b/packages/app/src/firebaseServerApp.ts @@ -30,7 +30,8 @@ import { base64Decode } from '@firebase/util'; export class FirebaseServerAppImpl extends FirebaseAppImpl - implements FirebaseServerApp { + implements FirebaseServerApp +{ private readonly _serverConfig: FirebaseServerAppSettings; private _finalizationRegistry: FinalizationRegistry | null; private _refCount: number; @@ -69,17 +70,20 @@ export class FirebaseServerAppImpl }; // Parse the installationAuthToken if provided. + this._installationsId = null; if (this._serverConfig.installationsAuthToken !== undefined) { - const thirdPart = this._serverConfig.installationsAuthToken.split(".")[1].split(".")[0]; - const decodedToken = base64Decode(thirdPart); - const tokenJSON = JSON.parse(decodedToken ? decodedToken : ""); - if (!decodedToken || !tokenJSON || tokenJSON.fid === undefined) { - throw ERROR_FACTORY.create(AppError.INVALID_SERVER_APP_INSTALLATIONS_AUTH_TOKEN); - } else { + try { + const decodedToken = base64Decode( + this._serverConfig.installationsAuthToken.split('.')[1] + ); + const tokenJSON = JSON.parse(decodedToken ? decodedToken : ''); this._installationsId = tokenJSON.fid; + } catch (e) { + console.warn(e); + } + if (!this._installationsId) { + console.error('INVALID SERVER APP INSTALLATIONS AUTH TOKEN!'); } - } else { - this._installationsId = null; } this._finalizationRegistry = null; From 41382edee7e1412428ad94c34c990b70ff6febd1 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Thu, 31 Oct 2024 10:51:45 -0400 Subject: [PATCH 04/13] error updates --- packages/installations/src/api/get-id.ts | 4 +++- packages/installations/src/api/get-token.ts | 6 ++++-- packages/installations/src/util/errors.ts | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/installations/src/api/get-id.ts b/packages/installations/src/api/get-id.ts index ae25099b032..7ccfe618749 100644 --- a/packages/installations/src/api/get-id.ts +++ b/packages/installations/src/api/get-id.ts @@ -32,7 +32,9 @@ import { ERROR_FACTORY, ErrorCode } from '../util/errors'; export async function getId(installations: Installations): Promise { if (_isFirebaseServerApp(installations.app)) { if (!installations.app.installationsId) { - throw ERROR_FACTORY.create(ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN); + throw ERROR_FACTORY.create( + ErrorCode.SERVER_APP_MISSING_INSTALLATIONS_AUTH_TOKEN + ); } return installations.app.installationsId; } diff --git a/packages/installations/src/api/get-token.ts b/packages/installations/src/api/get-token.ts index 354919c5014..0c307dfc442 100644 --- a/packages/installations/src/api/get-token.ts +++ b/packages/installations/src/api/get-token.ts @@ -35,8 +35,10 @@ export async function getToken( forceRefresh = false ): Promise { if (_isFirebaseServerApp(installations.app)) { - if (installations.app.installationsAuthToken === undefined) { - throw ERROR_FACTORY.create(ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN); + if (!installations.app.installationsAuthToken) { + throw ERROR_FACTORY.create( + ErrorCode.SERVER_APP_MISSING_INSTALLATIONS_AUTH_TOKEN + ); } return installations.app.installationsAuthToken; } diff --git a/packages/installations/src/util/errors.ts b/packages/installations/src/util/errors.ts index 88f40f26e9e..ac1efef2b02 100644 --- a/packages/installations/src/util/errors.ts +++ b/packages/installations/src/util/errors.ts @@ -25,7 +25,7 @@ export const enum ErrorCode { REQUEST_FAILED = 'request-failed', APP_OFFLINE = 'app-offline', DELETE_PENDING_REGISTRATION = 'delete-pending-registration', - SERVER_APP_MISSING_AUTH_TOKEN = 'server-app-missing-auth-token' + SERVER_APP_MISSING_INSTALLATIONS_AUTH_TOKEN = 'server-app-missing-installatoins-auth-token' } const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { @@ -38,7 +38,7 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { [ErrorCode.APP_OFFLINE]: 'Could not process request. Application offline.', [ErrorCode.DELETE_PENDING_REGISTRATION]: "Can't delete installation while there is a pending registration request.", - [ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN]: + [ErrorCode.SERVER_APP_MISSING_INSTALLATIONS_AUTH_TOKEN]: 'The instance of FirebaseServerApp was not initialized with a valid installationsAuthToken' }; From c6fc558850e7adfe3ad5a53fd547901c3dfd40b8 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Thu, 31 Oct 2024 11:10:57 -0400 Subject: [PATCH 05/13] remove redundant storage of installationsAuthId --- common/api-review/app.api.md | 3 --- packages/app/src/firebaseServerApp.ts | 9 --------- packages/app/src/public-types.ts | 5 ++++- packages/installations/src/api/get-token.ts | 4 ++-- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/common/api-review/app.api.md b/common/api-review/app.api.md index 864a9ff56e6..06b026e34ad 100644 --- a/common/api-review/app.api.md +++ b/common/api-review/app.api.md @@ -73,9 +73,6 @@ export interface FirebaseOptions { // @public export interface FirebaseServerApp extends FirebaseApp { - // (undocumented) - readonly installationsAuthToken: string | null; - // (undocumented) readonly installationsId: string | null; name: string; readonly settings: FirebaseServerAppSettings; diff --git a/packages/app/src/firebaseServerApp.ts b/packages/app/src/firebaseServerApp.ts index ad55a10bc04..a6122e24d92 100644 --- a/packages/app/src/firebaseServerApp.ts +++ b/packages/app/src/firebaseServerApp.ts @@ -144,15 +144,6 @@ export class FirebaseServerAppImpl return this._serverConfig; } - get installationsAuthToken(): string | null { - this.checkDestroyed(); - if (this._serverConfig.installationsAuthToken !== undefined) { - return this._serverConfig.installationsAuthToken; - } else { - return null; - } - } - get installationsId(): string | null { this.checkDestroyed(); return this._installationsId; diff --git a/packages/app/src/public-types.ts b/packages/app/src/public-types.ts index 26e4505443c..c5423dd144f 100644 --- a/packages/app/src/public-types.ts +++ b/packages/app/src/public-types.ts @@ -101,8 +101,11 @@ export interface FirebaseServerApp extends FirebaseApp { */ readonly settings: FirebaseServerAppSettings; + /** + * The parsed Firebase Installations Id token if a `installationsAuthToken` was provided to + * {@link (initializeServerApp:1) | initializeServerApp()}. Null otherwise. + */ readonly installationsId: string | null; - readonly installationsAuthToken: string | null; } /** diff --git a/packages/installations/src/api/get-token.ts b/packages/installations/src/api/get-token.ts index 0c307dfc442..b470ce1d863 100644 --- a/packages/installations/src/api/get-token.ts +++ b/packages/installations/src/api/get-token.ts @@ -35,12 +35,12 @@ export async function getToken( forceRefresh = false ): Promise { if (_isFirebaseServerApp(installations.app)) { - if (!installations.app.installationsAuthToken) { + if (!installations.app.settings.installationsAuthToken) { throw ERROR_FACTORY.create( ErrorCode.SERVER_APP_MISSING_INSTALLATIONS_AUTH_TOKEN ); } - return installations.app.installationsAuthToken; + return installations.app.settings.installationsAuthToken; } const installationsImpl = installations as FirebaseInstallationsImpl; await completeInstallationRegistration(installationsImpl); From 9e3fe1b1c643a35ee50e769ca1b23e2b495702ab Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Fri, 1 Nov 2024 14:41:56 -0400 Subject: [PATCH 06/13] docgen --- docs-devsite/app.firebaseserverapp.md | 11 +++++++++++ docs-devsite/app.firebaseserverappsettings.md | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/docs-devsite/app.firebaseserverapp.md b/docs-devsite/app.firebaseserverapp.md index 66b51c45fb2..7a9f4522649 100644 --- a/docs-devsite/app.firebaseserverapp.md +++ b/docs-devsite/app.firebaseserverapp.md @@ -25,9 +25,20 @@ export interface FirebaseServerApp extends FirebaseApp | Property | Type | Description | | --- | --- | --- | +| [installationsId](./app.firebaseserverapp.md#firebaseserverappinstallationsid) | string \| null | The parsed Firebase Installations Id token if a installationsAuthToken was provided to [initializeServerApp()](./app.md#initializeserverapp_30ab697). Null otherwise. | | [name](./app.firebaseserverapp.md#firebaseserverappname) | string | There is no getApp() operation for FirebaseServerApp, so the name is not relevant for applications. However, it may be used internally, and is declared here so that FirebaseServerApp conforms to the FirebaseApp interface. | | [settings](./app.firebaseserverapp.md#firebaseserverappsettings) | [FirebaseServerAppSettings](./app.firebaseserverappsettings.md#firebaseserverappsettings_interface) | The (read-only) configuration settings for this server app. These are the original parameters given in [initializeServerApp()](./app.md#initializeserverapp_30ab697). | +## FirebaseServerApp.installationsId + +The parsed Firebase Installations Id token if a `installationsAuthToken` was provided to [initializeServerApp()](./app.md#initializeserverapp_30ab697). Null otherwise. + +Signature: + +```typescript +readonly installationsId: string | null; +``` + ## FirebaseServerApp.name There is no `getApp()` operation for `FirebaseServerApp`, so the name is not relevant for applications. However, it may be used internally, and is declared here so that `FirebaseServerApp` conforms to the `FirebaseApp` interface. diff --git a/docs-devsite/app.firebaseserverappsettings.md b/docs-devsite/app.firebaseserverappsettings.md index bc46c5292d0..4fd27c75d7c 100644 --- a/docs-devsite/app.firebaseserverappsettings.md +++ b/docs-devsite/app.firebaseserverappsettings.md @@ -24,6 +24,7 @@ export interface FirebaseServerAppSettings extends OmitInvoking 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.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 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. | +| [installationsAuthToken](./app.firebaseserverappsettings.md#firebaseserverappsettingsinstallationsauthtoken) | string | An optional Installations Auth token which allows the use of Remote Config SDK in SSR enviornments.If provided, the FirebaseServerApp will attempt to parse the Installations id from the token.If the token is deemed to be malformed then an error will be thrown during the invocation of initializeServerApp.If the the Installations Id and the provided installationsAuthToken are successfully parsed, then they will be used by the Installations implementation when getToken and getId are invoked.Attempting to use Remote Config without providing an installationsAuthToken here will cause Installations to throw errors when Remote Config attempts to query the Installations id and authToken. | | [releaseOnDeref](./app.firebaseserverappsettings.md#firebaseserverappsettingsreleaseonderef) | object | An optional object. If provided, the Firebase SDK uses a FinalizationRegistry object to monitor the garbage collection status of the provided object. The Firebase SDK releases its reference on the FirebaseServerApp instance when the provided releaseOnDeref 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 FirebaseServerApp 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 FirebaseServerApp instance by invoking deleteApp.If the application provides an object in this parameter, but the application is executed in a JavaScript engine that predates the support of FinalizationRegistry (introduced in node v14.6.0, for instance), then an error is thrown at FirebaseServerApp initialization. | ## FirebaseServerAppSettings.authIdToken @@ -42,6 +43,24 @@ If a user is successfully signed in, then the Auth instance's `onAuthStateChange authIdToken?: string; ``` +## FirebaseServerAppSettings.installationsAuthToken + +An optional Installations Auth token which allows the use of Remote Config SDK in SSR enviornments. + +If provided, the `FirebaseServerApp` will attempt to parse the Installations id from the token. + +If the token is deemed to be malformed then an error will be thrown during the invocation of `initializeServerApp`. + +If the the Installations Id and the provided `installationsAuthToken` are successfully parsed, then they will be used by the Installations implementation when `getToken` and `getId` are invoked. + +Attempting to use Remote Config without providing an `installationsAuthToken` here will cause Installations to throw errors when Remote Config attempts to query the Installations id and authToken. + +Signature: + +```typescript +installationsAuthToken?: string; +``` + ## FirebaseServerAppSettings.releaseOnDeref An optional object. If provided, the Firebase SDK uses a `FinalizationRegistry` object to monitor the garbage collection status of the provided object. The Firebase SDK releases its reference on the `FirebaseServerApp` instance when the provided `releaseOnDeref` object is garbage collected. From acda70549cc9fc3a3391dcd2ae0cf41d2812e352 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Fri, 1 Nov 2024 14:42:06 -0400 Subject: [PATCH 07/13] FirebaseServerApp unit tests --- packages/app/src/firebaseServerApp.test.ts | 84 +++++++++++++++++++++- packages/app/src/firebaseServerApp.ts | 6 +- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/packages/app/src/firebaseServerApp.test.ts b/packages/app/src/firebaseServerApp.test.ts index bf2da5c06d5..689d76cfae0 100644 --- a/packages/app/src/firebaseServerApp.test.ts +++ b/packages/app/src/firebaseServerApp.test.ts @@ -19,7 +19,19 @@ import { expect } from 'chai'; import '../test/setup'; import { ComponentContainer } from '@firebase/component'; import { FirebaseServerAppImpl } from './firebaseServerApp'; -import { FirebaseServerAppSettings } from './public-types'; +import { FirebaseApp, FirebaseServerAppSettings } from './public-types'; + +const VALID_INSTATLLATIONS_AUTH_TOKEN_SECONDPART: string = + 'foo.eyJhcHBJZCI6IjE6MDAwMDAwMDAwMDAwOndlYjowMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwiZXhwIjoiMDAwMDAwMD' + + 'AwMCIsImZpZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJwcm9qZWN0TnVtYmVyIjoiMDAwMDAwMDAwMDAwIn0.foo'; + +const INVALID_INSTATLLATIONS_AUTH_TOKEN: string = + 'foo.eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9eyJhcHBJZCI6IjE6MDAwMDAwMDAwMDAwOndlYjowMDAwMDAwMDAwMD' + + 'AwMDAwMDAwMDAwIiwiZXhwIjowMDAwMDAwMDAwLCJwcm9qZWN0TnVtYmVyIjowMDAwMDAwMDAwMDB9.foo'; + +const INVALID_INSTATLLATIONS_AUTH_TOKEN_ONE_PART: string = + 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9eyJhcHBJZCI6IjE6MDAwMDAwMDAwMDAwOndlYjowMDAwMDAwMDAwMD' + + 'AwMDAwMDAwMDAwIiwiZXhwIjowMDAwMDAwMDAwLCJwcm9qZWN0TnVtYmVyIjowMDAwMDAwMDAwMDB9'; describe('FirebaseServerApp', () => { it('has various accessors', () => { @@ -155,4 +167,74 @@ describe('FirebaseServerApp', () => { expect(JSON.stringify(app)).to.eql(undefined); }); + + it('parses valid installationsAuthToken', () => { + const options = { + apiKey: 'APIKEY' + }; + + const serverAppSettings: FirebaseServerAppSettings = { + automaticDataCollectionEnabled: false, + installationsAuthToken: VALID_INSTATLLATIONS_AUTH_TOKEN_SECONDPART + }; + + let app: FirebaseApp | null = null; + try { + app = new FirebaseServerAppImpl( + options, + serverAppSettings, + 'testName', + new ComponentContainer('test') + ); + } catch (e) {} + expect(app).to.not.be.null; + }); + + it('invalid installationsAuthToken throws', () => { + const options = { + apiKey: 'APIKEY' + }; + + const serverAppSettings: FirebaseServerAppSettings = { + automaticDataCollectionEnabled: false, + installationsAuthToken: INVALID_INSTATLLATIONS_AUTH_TOKEN + }; + + let failed = false; + try { + new FirebaseServerAppImpl( + options, + serverAppSettings, + 'testName', + new ComponentContainer('test') + ); + } catch (e) { + failed = true; + } + expect(failed).to.be.true; + }); + + it('invalid single part installationsAuthToken throws', () => { + const options = { + apiKey: 'APIKEY' + }; + + const serverAppSettings: FirebaseServerAppSettings = { + automaticDataCollectionEnabled: false, + installationsAuthToken: INVALID_INSTATLLATIONS_AUTH_TOKEN_ONE_PART + }; + + let failed = false; + try { + new FirebaseServerAppImpl( + options, + serverAppSettings, + 'testName', + new ComponentContainer('test') + ); + } catch (e) { + failed = true; + } + expect(failed).to.be.true; + }); }); diff --git a/packages/app/src/firebaseServerApp.ts b/packages/app/src/firebaseServerApp.ts index a6122e24d92..80a48744d4b 100644 --- a/packages/app/src/firebaseServerApp.ts +++ b/packages/app/src/firebaseServerApp.ts @@ -81,8 +81,10 @@ export class FirebaseServerAppImpl } catch (e) { console.warn(e); } - if (!this._installationsId) { - console.error('INVALID SERVER APP INSTALLATIONS AUTH TOKEN!'); + if (this._installationsId === null) { + throw ERROR_FACTORY.create( + AppError.INVALID_SERVER_APP_INSTALLATIONS_AUTH_TOKEN + ); } } From 10348f8c801ad6dd171e744d27d6e2e175ca55a6 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 4 Nov 2024 11:24:25 -0500 Subject: [PATCH 08/13] Added server / node tests to Installations. --- packages/installations/karma.conf.js | 1 + packages/installations/package.json | 8 ++-- .../src/api/get-id-server-app.test.ts | 42 ++++++++++++++++++ .../src/api/get-token-server-app.test.ts | 44 +++++++++++++++++++ .../src/testing/fake-generators.ts | 26 +++++++++-- packages/installations/src/util/errors.ts | 2 +- 6 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 packages/installations/src/api/get-id-server-app.test.ts create mode 100644 packages/installations/src/api/get-token-server-app.test.ts diff --git a/packages/installations/karma.conf.js b/packages/installations/karma.conf.js index 1699a0681ec..71ad8b9362f 100644 --- a/packages/installations/karma.conf.js +++ b/packages/installations/karma.conf.js @@ -24,6 +24,7 @@ module.exports = function (config) { ...karmaBase, // files to load into karma files, + exclude: ['src/**/*-server-app.test.ts'], // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['mocha'] diff --git a/packages/installations/package.json b/packages/installations/package.json index 78b6dacc6da..48ec9b9a952 100644 --- a/packages/installations/package.json +++ b/packages/installations/package.json @@ -25,9 +25,11 @@ "build:deps": "lerna run --scope @firebase/installations --include-dependencies build", "build:release": "yarn build && yarn typings:public", "dev": "rollup -c -w", - "test": "yarn type-check && yarn test:karma && yarn lint", - "test:ci": "node ../../scripts/run_tests_in_ci.js", - "test:karma": "karma start", + "test": "yarn type-check && yarn test:all && yarn lint", + "test:ci": "node ../../scripts/run_tests_in_ci.js -s test:all", + "test:all": "run-p --npm-path npm test:browser test:node", + "test:browser" : "karma start", + "test:node": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha src/**/*server-app.test.ts --config ../../config/mocharc.node.js", "test:debug": "karma start --browsers=Chrome --auto-watch", "trusted-type-check": "tsec -p tsconfig.json --noEmit", "type-check": "tsc -p . --noEmit", diff --git a/packages/installations/src/api/get-id-server-app.test.ts b/packages/installations/src/api/get-id-server-app.test.ts new file mode 100644 index 00000000000..d16275b29f4 --- /dev/null +++ b/packages/installations/src/api/get-id-server-app.test.ts @@ -0,0 +1,42 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { getId } from './get-id'; +import { FAKE_INSTALLATIONS_ID, getFakeInstallations, getFakeServerApp } from '../testing/fake-generators'; + +describe('getId-serverapp', () => { + it('getId with firebaseServerApp with authIdToken returns valid id', async() => { + const installationsAuthToken = "fakeToken"; + const serverApp = getFakeServerApp(installationsAuthToken); + const installations = getFakeInstallations(serverApp); + const fid = await getId(installations); + expect(fid).to.equal(FAKE_INSTALLATIONS_ID); + }); + it('getId with firebaseServerApp without authIdToken throws', async() => { + const serverApp = getFakeServerApp(); + const installations = getFakeInstallations(serverApp); + let fails = false; + try { + await getId(installations); + } catch (e) { + console.error(e); + fails = true; + } + expect(fails).to.be.true; + }); +}); diff --git a/packages/installations/src/api/get-token-server-app.test.ts b/packages/installations/src/api/get-token-server-app.test.ts new file mode 100644 index 00000000000..33b3a4d3aed --- /dev/null +++ b/packages/installations/src/api/get-token-server-app.test.ts @@ -0,0 +1,44 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from 'chai'; +import { getToken } from './get-token'; +import { getFakeInstallations, getFakeServerApp } from '../testing/fake-generators'; +import chaiAsPromised from 'chai-as-promised'; + +use(chaiAsPromised); + +describe('getToken-serverapp', () => { + it('getToken with firebaseServerApp with authIdToken returns valid token', async () => { + const installationsAuthToken = "fakeToken.abc123"; + const serverApp = getFakeServerApp(installationsAuthToken); + const installations = getFakeInstallations(serverApp); + const token = await getToken(installations); + expect(token).to.equal(installationsAuthToken); + }); + it('getToken with firebaseServerApp without authIdToken throws', async () => { + const serverApp = getFakeServerApp(); + const installations = getFakeInstallations(serverApp); + let fails = false; + try { + await getToken(installations); + } catch (e) { + fails = true; + } + expect(fails).to.be.true; + }); +}); diff --git a/packages/installations/src/testing/fake-generators.ts b/packages/installations/src/testing/fake-generators.ts index 6309228d72b..a29f0b12eb6 100644 --- a/packages/installations/src/testing/fake-generators.ts +++ b/packages/installations/src/testing/fake-generators.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { FirebaseApp } from '@firebase/app'; +import { FirebaseApp, FirebaseServerApp } from '@firebase/app'; import { Component, ComponentContainer, @@ -27,6 +27,8 @@ import { AppConfig } from '../interfaces/installation-impl'; +export const FAKE_INSTALLATIONS_ID = 'abc123'; + export function getFakeApp(): FirebaseApp { return { name: 'appName', @@ -43,13 +45,29 @@ export function getFakeApp(): FirebaseApp { }; } +export function getFakeServerApp( + installationsAuthToken: string | null = null +): FirebaseServerApp { + const app = getFakeApp() as any; + app.settings = { + automaticDataCollectionEnabled: true + }; + if (installationsAuthToken !== null) { + app.settings.installationsAuthToken = installationsAuthToken; + app.installationsId = FAKE_INSTALLATIONS_ID; + } + return app; +} + export function getFakeAppConfig( customValues: Partial = {} ): AppConfig { return { ...extractAppConfig(getFakeApp()), ...customValues }; } -export function getFakeInstallations(): FirebaseInstallationsImpl { +export function getFakeInstallations( + app?: FirebaseApp +): FirebaseInstallationsImpl { const container = new ComponentContainer('test'); container.addComponent( new Component( @@ -61,9 +79,9 @@ export function getFakeInstallations(): FirebaseInstallationsImpl { ComponentType.PRIVATE ) ); - + const configuredApp: FirebaseApp = app ? app : getFakeApp(); return { - app: getFakeApp(), + app: configuredApp, appConfig: getFakeAppConfig(), heartbeatServiceProvider: container.getProvider('heartbeat'), _delete: () => { diff --git a/packages/installations/src/util/errors.ts b/packages/installations/src/util/errors.ts index ac1efef2b02..a09b7f22ee8 100644 --- a/packages/installations/src/util/errors.ts +++ b/packages/installations/src/util/errors.ts @@ -25,7 +25,7 @@ export const enum ErrorCode { REQUEST_FAILED = 'request-failed', APP_OFFLINE = 'app-offline', DELETE_PENDING_REGISTRATION = 'delete-pending-registration', - SERVER_APP_MISSING_INSTALLATIONS_AUTH_TOKEN = 'server-app-missing-installatoins-auth-token' + SERVER_APP_MISSING_INSTALLATIONS_AUTH_TOKEN = 'server-app-missing-installations-auth-token' } const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { From 6f4f745e256db8e8030969bcbb948bf8068469db Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 4 Nov 2024 14:08:17 -0500 Subject: [PATCH 09/13] Add a todo for token verification --- packages/app/src/firebaseServerApp.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/app/src/firebaseServerApp.ts b/packages/app/src/firebaseServerApp.ts index 80a48744d4b..bfa6a99d16c 100644 --- a/packages/app/src/firebaseServerApp.ts +++ b/packages/app/src/firebaseServerApp.ts @@ -70,6 +70,7 @@ export class FirebaseServerAppImpl }; // Parse the installationAuthToken if provided. + // TODO: kick off the token verification process. this._installationsId = null; if (this._serverConfig.installationsAuthToken !== undefined) { try { From e1913e38c822eb04cc0a11bf33d35bad71cdd3e4 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 4 Nov 2024 14:16:46 -0500 Subject: [PATCH 10/13] format --- .../src/api/get-id-server-app.test.ts | 14 +++++++++----- .../src/api/get-token-server-app.test.ts | 9 ++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/installations/src/api/get-id-server-app.test.ts b/packages/installations/src/api/get-id-server-app.test.ts index d16275b29f4..b29b018eff6 100644 --- a/packages/installations/src/api/get-id-server-app.test.ts +++ b/packages/installations/src/api/get-id-server-app.test.ts @@ -17,23 +17,27 @@ import { expect } from 'chai'; import { getId } from './get-id'; -import { FAKE_INSTALLATIONS_ID, getFakeInstallations, getFakeServerApp } from '../testing/fake-generators'; +import { + FAKE_INSTALLATIONS_ID, + getFakeInstallations, + getFakeServerApp +} from '../testing/fake-generators'; describe('getId-serverapp', () => { - it('getId with firebaseServerApp with authIdToken returns valid id', async() => { - const installationsAuthToken = "fakeToken"; + it('getId with firebaseServerApp with authIdToken returns valid id', async () => { + const installationsAuthToken = 'fakeToken'; const serverApp = getFakeServerApp(installationsAuthToken); const installations = getFakeInstallations(serverApp); const fid = await getId(installations); expect(fid).to.equal(FAKE_INSTALLATIONS_ID); }); - it('getId with firebaseServerApp without authIdToken throws', async() => { + it('getId with firebaseServerApp without authIdToken throws', async () => { const serverApp = getFakeServerApp(); const installations = getFakeInstallations(serverApp); let fails = false; try { await getId(installations); - } catch (e) { + } catch (e) { console.error(e); fails = true; } diff --git a/packages/installations/src/api/get-token-server-app.test.ts b/packages/installations/src/api/get-token-server-app.test.ts index 33b3a4d3aed..d4d8b3ffa35 100644 --- a/packages/installations/src/api/get-token-server-app.test.ts +++ b/packages/installations/src/api/get-token-server-app.test.ts @@ -17,14 +17,17 @@ import { expect, use } from 'chai'; import { getToken } from './get-token'; -import { getFakeInstallations, getFakeServerApp } from '../testing/fake-generators'; +import { + getFakeInstallations, + getFakeServerApp +} from '../testing/fake-generators'; import chaiAsPromised from 'chai-as-promised'; use(chaiAsPromised); describe('getToken-serverapp', () => { it('getToken with firebaseServerApp with authIdToken returns valid token', async () => { - const installationsAuthToken = "fakeToken.abc123"; + const installationsAuthToken = 'fakeToken'; const serverApp = getFakeServerApp(installationsAuthToken); const installations = getFakeInstallations(serverApp); const token = await getToken(installations); @@ -36,7 +39,7 @@ describe('getToken-serverapp', () => { let fails = false; try { await getToken(installations); - } catch (e) { + } catch (e) { fails = true; } expect(fails).to.be.true; From ae3ccfdc03b3f09979447de6b014766b7649234d Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 18 Nov 2024 11:27:47 -0500 Subject: [PATCH 11/13] Throw in fetch in non browser environments --- packages/remote-config/src/client/rest_client.ts | 6 ++++++ packages/remote-config/src/errors.ts | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/remote-config/src/client/rest_client.ts b/packages/remote-config/src/client/rest_client.ts index 87fdae3c3d6..773824e916c 100644 --- a/packages/remote-config/src/client/rest_client.ts +++ b/packages/remote-config/src/client/rest_client.ts @@ -24,6 +24,7 @@ import { import { ERROR_FACTORY, ErrorCode } from '../errors'; import { getUserLanguage } from '../language'; import { _FirebaseInstallationsInternal } from '@firebase/installations'; +import { isBrowser } from '@firebase/util'; /** * Defines request body parameters required to call the fetch API: @@ -65,8 +66,13 @@ export class RestClient implements RemoteConfigFetchClient { * @throws a {@link ErrorCode.FETCH_PARSE} error if {@link Response#json} can't parse the * fetch response. * @throws a {@link ErrorCode.FETCH_STATUS} error if the service returns an HTTP error status. + * @throws a {@link ErrorCode.REQUIRES_BROWSER_ENVIRONMENT} error if the invoked in a non browser + * environment. */ async fetch(request: FetchRequest): Promise { + if (!isBrowser()) { + throw ERROR_FACTORY.create(ErrorCode.REQUIRES_BROWSER_ENVIRONMENT); + } const [installationId, installationToken] = await Promise.all([ this.firebaseInstallations.getId(), this.firebaseInstallations.getToken() diff --git a/packages/remote-config/src/errors.ts b/packages/remote-config/src/errors.ts index eac9a25657b..21f035dc823 100644 --- a/packages/remote-config/src/errors.ts +++ b/packages/remote-config/src/errors.ts @@ -31,7 +31,8 @@ export const enum ErrorCode { FETCH_THROTTLE = 'fetch-throttle', FETCH_PARSE = 'fetch-client-parse', FETCH_STATUS = 'fetch-status', - INDEXED_DB_UNAVAILABLE = 'indexed-db-unavailable' + INDEXED_DB_UNAVAILABLE = 'indexed-db-unavailable', + REQUIRES_BROWSER_ENVIRONMENT = 'requires-browser-environment' } const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { @@ -67,7 +68,9 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = { [ErrorCode.FETCH_STATUS]: 'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.', [ErrorCode.INDEXED_DB_UNAVAILABLE]: - 'Indexed DB is not supported by current browser' + 'Indexed DB is not supported by current browser', + [ErrorCode.REQUIRES_BROWSER_ENVIRONMENT]: + 'The requested operation must be executed in a browser environment' }; // Note this is effectively a type system binding a code to params. This approach overlaps with the From 74af5ba45eac280b30e1c3ccd31e5f2ec4dc6512 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Tue, 19 Nov 2024 10:34:54 -0500 Subject: [PATCH 12/13] changeset --- .changeset/nine-clouds-hammer.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/nine-clouds-hammer.md diff --git a/.changeset/nine-clouds-hammer.md b/.changeset/nine-clouds-hammer.md new file mode 100644 index 00000000000..c84e0a5ba4e --- /dev/null +++ b/.changeset/nine-clouds-hammer.md @@ -0,0 +1,7 @@ +--- +'@firebase/installations': minor +'@firebase/app': minor +'@firebase/remote-config': patch +--- + +Add `installationsAuthToken` as an optional FirebaseServerApp variable. If present, then Installations `getId` and `getToken` will use the provided value instead of initializing the Installations SDK to retrieve those values dynamically. This should unlock SDKs that require these Installations values in a server environment where the Installations SDK isn't supported. From da58c9a58a6e307ca1adb5c7f3ab14aa6fa6ac33 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Fri, 22 Nov 2024 15:17:43 -0500 Subject: [PATCH 13/13] Add 'firebase': minor to changeset. --- .changeset/nine-clouds-hammer.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/nine-clouds-hammer.md b/.changeset/nine-clouds-hammer.md index c84e0a5ba4e..be025e0c5a4 100644 --- a/.changeset/nine-clouds-hammer.md +++ b/.changeset/nine-clouds-hammer.md @@ -1,6 +1,7 @@ --- '@firebase/installations': minor '@firebase/app': minor +'firebase': minor '@firebase/remote-config': patch ---