Skip to content

Commit 500168c

Browse files
committed
Initial Impl
1 parent d4cb3f8 commit 500168c

File tree

7 files changed

+86
-6
lines changed

7 files changed

+86
-6
lines changed

common/api-review/app.api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,18 @@ export interface FirebaseOptions {
7373

7474
// @public
7575
export interface FirebaseServerApp extends FirebaseApp {
76+
// (undocumented)
77+
installationsAuthToken?: string;
78+
// (undocumented)
79+
installationsId?: string;
7680
name: string;
7781
readonly settings: FirebaseServerAppSettings;
7882
}
7983

8084
// @public
8185
export interface FirebaseServerAppSettings extends Omit<FirebaseAppSettings, 'name'> {
8286
authIdToken?: string;
87+
installationsAuthToken?: string;
8388
releaseOnDeref?: object;
8489
}
8590

packages/app/src/errors.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export const enum AppError {
3131
IDB_WRITE = 'idb-set',
3232
IDB_DELETE = 'idb-delete',
3333
FINALIZATION_REGISTRY_NOT_SUPPORTED = 'finalization-registry-not-supported',
34-
INVALID_SERVER_APP_ENVIRONMENT = 'invalid-server-app-environment'
34+
INVALID_SERVER_APP_ENVIRONMENT = 'invalid-server-app-environment',
35+
INVALID_SERVER_APP_INSTALLATIONS_AUTH_TOKEN = 'invalid-server-installations-auth-token'
3536
}
3637

3738
const ERRORS: ErrorMap<AppError> = {
@@ -61,7 +62,9 @@ const ERRORS: ErrorMap<AppError> = {
6162
[AppError.FINALIZATION_REGISTRY_NOT_SUPPORTED]:
6263
'FirebaseServerApp deleteOnDeref field defined but the JS runtime does not support FinalizationRegistry.',
6364
[AppError.INVALID_SERVER_APP_ENVIRONMENT]:
64-
'FirebaseServerApp is not for use in browser environments.'
65+
'FirebaseServerApp is not for use in browser environments.',
66+
[AppError.INVALID_SERVER_APP_INSTALLATIONS_AUTH_TOKEN]:
67+
'FirebaseServerApp could not initialize due to an invalid Installations auth token'
6568
};
6669

6770
interface ErrorParams {

packages/app/src/firebaseServerApp.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ 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';
2930

3031
export class FirebaseServerAppImpl
3132
extends FirebaseAppImpl
32-
implements FirebaseServerApp
33-
{
33+
implements FirebaseServerApp {
3434
private readonly _serverConfig: FirebaseServerAppSettings;
3535
private _finalizationRegistry: FinalizationRegistry<object> | null;
3636
private _refCount: number;
37+
private _installationsId: string | null;
3738

3839
constructor(
3940
options: FirebaseOptions | FirebaseAppImpl,
@@ -67,6 +68,20 @@ export class FirebaseServerAppImpl
6768
...serverConfig
6869
};
6970

71+
// Parse the installationAuthToken if provided.
72+
if (this._serverConfig.installationsAuthToken !== undefined) {
73+
const thirdPart = this._serverConfig.installationsAuthToken.split(".")[1].split(".")[0];
74+
const decodedToken = base64Decode(thirdPart);
75+
const tokenJSON = JSON.parse(decodedToken ? decodedToken : "");
76+
if (!decodedToken || !tokenJSON || tokenJSON.fid === undefined) {
77+
throw ERROR_FACTORY.create(AppError.INVALID_SERVER_APP_INSTALLATIONS_AUTH_TOKEN);
78+
} else {
79+
this._installationsId = tokenJSON.fid;
80+
}
81+
} else {
82+
this._installationsId = null;
83+
}
84+
7085
this._finalizationRegistry = null;
7186
if (typeof FinalizationRegistry !== 'undefined') {
7287
this._finalizationRegistry = new FinalizationRegistry(() => {
@@ -125,6 +140,20 @@ export class FirebaseServerAppImpl
125140
return this._serverConfig;
126141
}
127142

143+
get installationsAuthToken(): string | null {
144+
this.checkDestroyed();
145+
if (this._serverConfig.installationsAuthToken !== undefined) {
146+
return this._serverConfig.installationsAuthToken;
147+
} else {
148+
return null;
149+
}
150+
}
151+
152+
get installationsId(): string | null {
153+
this.checkDestroyed();
154+
return this._installationsId;
155+
}
156+
128157
/**
129158
* This function will throw an Error if the App has already been deleted -
130159
* use before performing API actions on the App.

packages/app/src/public-types.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ export interface FirebaseServerApp extends FirebaseApp {
100100
* ```
101101
*/
102102
readonly settings: FirebaseServerAppSettings;
103+
104+
readonly installationsId : string | null;
105+
readonly installationsAuthToken : string | null;
103106
}
104107

105108
/**
@@ -196,6 +199,26 @@ export interface FirebaseServerAppSettings
196199
*/
197200
authIdToken?: string;
198201

202+
/**
203+
* An optional Installations Auth token which allows the use of Remote Config SDK in
204+
* SSR enviornments.
205+
*
206+
* If provided, the `FirebaseServerApp` will attempt to parse the Installations id
207+
* from the token.
208+
*
209+
* If the token is deemed to be malformed then an error will be
210+
* thrown during the invocation of `initializeServerApp`.
211+
*
212+
* If the the Installations Id and the provided `installationsAuthToken` are successfully parsed,
213+
* then they will be used by the Installations implementation when `getToken` and `getId` are
214+
* invoked.
215+
*
216+
* Attempting to use Remote Config without providing an `installationsAuthToken` here will cause
217+
* Installations to throw errors when Remote Config attempts to query the Installations id and
218+
* authToken.
219+
*/
220+
installationsAuthToken?: string;
221+
199222
/**
200223
* An optional object. If provided, the Firebase SDK uses a `FinalizationRegistry`
201224
* object to monitor the garbage collection status of the provided object. The

packages/installations/src/api/get-id.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { getInstallationEntry } from '../helpers/get-installation-entry';
1919
import { refreshAuthToken } from '../helpers/refresh-auth-token';
2020
import { FirebaseInstallationsImpl } from '../interfaces/installation-impl';
2121
import { Installations } from '../interfaces/public-types';
22+
import { _isFirebaseServerApp } from '@firebase/app';
23+
import { ERROR_FACTORY, ErrorCode } from '../util/errors';
2224

2325
/**
2426
* Creates a Firebase Installation if there isn't one for the app and
@@ -28,6 +30,13 @@ import { Installations } from '../interfaces/public-types';
2830
* @public
2931
*/
3032
export async function getId(installations: Installations): Promise<string> {
33+
if(_isFirebaseServerApp(installations.app)) {
34+
if(!installations.app.installationsId) {
35+
throw ERROR_FACTORY.create(ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN);
36+
}
37+
return installations.app.installationsId;
38+
}
39+
3140
const installationsImpl = installations as FirebaseInstallationsImpl;
3241
const { installationEntry, registrationPromise } = await getInstallationEntry(
3342
installationsImpl

packages/installations/src/api/get-token.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { getInstallationEntry } from '../helpers/get-installation-entry';
1919
import { refreshAuthToken } from '../helpers/refresh-auth-token';
2020
import { FirebaseInstallationsImpl } from '../interfaces/installation-impl';
2121
import { Installations } from '../interfaces/public-types';
22+
import { _isFirebaseServerApp } from '@firebase/app';
23+
import { ERROR_FACTORY, ErrorCode } from '../util/errors';
2224

2325
/**
2426
* Returns a Firebase Installations auth token, identifying the current
@@ -32,6 +34,12 @@ export async function getToken(
3234
installations: Installations,
3335
forceRefresh = false
3436
): Promise<string> {
37+
if(_isFirebaseServerApp(installations.app)) {
38+
if(installations.app.installationsAuthToken === undefined) {
39+
throw ERROR_FACTORY.create(ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN);
40+
}
41+
return installations.app.installationsAuthToken;
42+
}
3543
const installationsImpl = installations as FirebaseInstallationsImpl;
3644
await completeInstallationRegistration(installationsImpl);
3745

packages/installations/src/util/errors.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export const enum ErrorCode {
2424
INSTALLATION_NOT_FOUND = 'installation-not-found',
2525
REQUEST_FAILED = 'request-failed',
2626
APP_OFFLINE = 'app-offline',
27-
DELETE_PENDING_REGISTRATION = 'delete-pending-registration'
27+
DELETE_PENDING_REGISTRATION = 'delete-pending-registration',
28+
SERVER_APP_MISSING_AUTH_TOKEN = 'server-app-missing-auth-token'
2829
}
2930

3031
const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = {
@@ -36,7 +37,9 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = {
3637
'{$requestName} request failed with error "{$serverCode} {$serverStatus}: {$serverMessage}"',
3738
[ErrorCode.APP_OFFLINE]: 'Could not process request. Application offline.',
3839
[ErrorCode.DELETE_PENDING_REGISTRATION]:
39-
"Can't delete installation while there is a pending registration request."
40+
"Can't delete installation while there is a pending registration request.",
41+
[ErrorCode.SERVER_APP_MISSING_AUTH_TOKEN]:
42+
"The instance of FirebaseServerApp was not initialized with a valid installationsAuthToken"
4043
};
4144

4245
interface ErrorParams {

0 commit comments

Comments
 (0)