Skip to content

Commit c2b737b

Browse files
Use Admin AuthTokenProvider when targeting Emulator (#3228)
1 parent 29afa33 commit c2b737b

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

.changeset/smooth-poets-leave.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"firebase": patch
3+
"@firebase/database": patch
4+
---
5+
6+
[fix] Instead of using production auth, the SDK will use test credentials
7+
to connect to the Emulator when the RTDB SDK is used via the Firebase
8+
Admin SDK.

packages/database/src/core/AuthTokenProvider.ts

+32-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,17 @@ import { Provider } from '@firebase/component';
2424
import { log, warn } from './util/util';
2525
import { FirebaseApp } from '@firebase/app-types';
2626

27+
export interface AuthTokenProvider {
28+
getToken(forceRefresh: boolean): Promise<FirebaseAuthTokenData>;
29+
addTokenChangeListener(listener: (token: string | null) => void): void;
30+
removeTokenChangeListener(listener: (token: string | null) => void): void;
31+
notifyForInvalidToken(): void;
32+
}
33+
2734
/**
2835
* Abstraction around FirebaseApp's token fetching capabilities.
2936
*/
30-
export class AuthTokenProvider {
37+
export class FirebaseAuthTokenProvider implements AuthTokenProvider {
3138
private auth_: FirebaseAuthInternal | null = null;
3239
constructor(
3340
private app_: FirebaseApp,
@@ -60,7 +67,7 @@ export class AuthTokenProvider {
6067
});
6168
}
6269

63-
addTokenChangeListener(listener: (token: string | null) => void) {
70+
addTokenChangeListener(listener: (token: string | null) => void): void {
6471
// TODO: We might want to wrap the listener and call it with no args to
6572
// avoid a leaky abstraction, but that makes removing the listener harder.
6673
if (this.auth_) {
@@ -73,13 +80,13 @@ export class AuthTokenProvider {
7380
}
7481
}
7582

76-
removeTokenChangeListener(listener: (token: string | null) => void) {
83+
removeTokenChangeListener(listener: (token: string | null) => void): void {
7784
this.authProvider_
7885
.get()
7986
.then(auth => auth.removeAuthTokenListener(listener));
8087
}
8188

82-
notifyForInvalidToken() {
89+
notifyForInvalidToken(): void {
8390
let errorMessage =
8491
'Provided authentication credentials for the app named "' +
8592
this.app_.name +
@@ -104,3 +111,24 @@ export class AuthTokenProvider {
104111
warn(errorMessage);
105112
}
106113
}
114+
115+
/* Auth token provider that the Admin SDK uses to connect to the Emulator. */
116+
export class EmulatorAdminTokenProvider implements AuthTokenProvider {
117+
private static EMULATOR_AUTH_TOKEN = 'owner';
118+
119+
getToken(forceRefresh: boolean): Promise<FirebaseAuthTokenData> {
120+
return Promise.resolve({
121+
accessToken: EmulatorAdminTokenProvider.EMULATOR_AUTH_TOKEN
122+
});
123+
}
124+
125+
addTokenChangeListener(listener: (token: string | null) => void): void {
126+
// Invoke the listener immediately to match the behavior in Firebase Auth
127+
// (see packages/auth/src/auth.js#L1807)
128+
listener(EmulatorAdminTokenProvider.EMULATOR_AUTH_TOKEN);
129+
}
130+
131+
removeTokenChangeListener(listener: (token: string | null) => void): void {}
132+
133+
notifyForInvalidToken(): void {}
134+
}

packages/database/src/core/Repo.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,8 @@ export class Repo {
8383
public repoInfo_: RepoInfo,
8484
forceRestClient: boolean,
8585
public app: FirebaseApp,
86-
authProvider: Provider<FirebaseAuthInternalName>
86+
authTokenProvider: AuthTokenProvider
8787
) {
88-
const authTokenProvider = new AuthTokenProvider(app, authProvider);
89-
9088
this.stats_ = StatsManager.getCollection(repoInfo_);
9189

9290
if (forceRestClient || beingCrawled()) {

packages/database/src/core/RepoManager.ts

+20-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
import { FirebaseApp } from '@firebase/app-types';
19-
import { safeGet } from '@firebase/util';
19+
import { safeGet, CONSTANTS } from '@firebase/util';
2020
import { Repo } from './Repo';
2121
import { fatal } from './util/util';
2222
import { parseRepoInfo } from './util/libs/parser';
@@ -26,6 +26,11 @@ import { Database } from '../api/Database';
2626
import { RepoInfo } from './RepoInfo';
2727
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
2828
import { Provider } from '@firebase/component';
29+
import {
30+
AuthTokenProvider,
31+
EmulatorAdminTokenProvider,
32+
FirebaseAuthTokenProvider
33+
} from './AuthTokenProvider';
2934

3035
/** @const {string} */
3136
const DATABASE_URL_OPTION = 'databaseURL';
@@ -108,16 +113,27 @@ export class RepoManager {
108113
let parsedUrl = parseRepoInfo(dbUrl);
109114
let repoInfo = parsedUrl.repoInfo;
110115

116+
let isEmulator: boolean;
117+
111118
let dbEmulatorHost: string | undefined = undefined;
112119
if (typeof process !== 'undefined') {
113120
dbEmulatorHost = process.env[FIREBASE_DATABASE_EMULATOR_HOST_VAR];
114121
}
122+
115123
if (dbEmulatorHost) {
124+
isEmulator = true;
116125
dbUrl = `http://${dbEmulatorHost}?ns=${repoInfo.namespace}`;
117126
parsedUrl = parseRepoInfo(dbUrl);
118127
repoInfo = parsedUrl.repoInfo;
128+
} else {
129+
isEmulator = !parsedUrl.repoInfo.secure;
119130
}
120131

132+
const authTokenProvider =
133+
CONSTANTS.NODE_ADMIN && isEmulator
134+
? new EmulatorAdminTokenProvider()
135+
: new FirebaseAuthTokenProvider(app, authProvider);
136+
121137
validateUrl('Invalid Firebase Database URL', 1, parsedUrl);
122138
if (!parsedUrl.path.isEmpty()) {
123139
fatal(
@@ -126,7 +142,7 @@ export class RepoManager {
126142
);
127143
}
128144

129-
const repo = this.createRepo(repoInfo, app, authProvider);
145+
const repo = this.createRepo(repoInfo, app, authTokenProvider);
130146

131147
return repo.database;
132148
}
@@ -159,7 +175,7 @@ export class RepoManager {
159175
createRepo(
160176
repoInfo: RepoInfo,
161177
app: FirebaseApp,
162-
authProvider: Provider<FirebaseAuthInternalName>
178+
authTokenProvider: AuthTokenProvider
163179
): Repo {
164180
let appRepos = safeGet(this.repos_, app.name);
165181

@@ -174,7 +190,7 @@ export class RepoManager {
174190
'Database initialized multiple times. Please make sure the format of the database URL matches with each database() call.'
175191
);
176192
}
177-
repo = new Repo(repoInfo, this.useRestClient_, app, authProvider);
193+
repo = new Repo(repoInfo, this.useRestClient_, app, authTokenProvider);
178194
appRepos[repoInfo.toURLString()] = repo;
179195

180196
return repo;

0 commit comments

Comments
 (0)