Skip to content

Commit eeb1dfa

Browse files
avolkovisam-gcyuchenshiFeiyang1
authored
Add a way to specify emulator host/port for Auth (#3810)
* Add a way to specify emulator host/port for Auth * Update API to take URI instead of hostname/port * Pull over more changes from internal repo * Fix a couple emulator related issues * Use window.emulatorURL for demo code * Remove debugger statement * Update packages/auth/src/auth.js * PR Feedback + typings * Add changeset * Update packages/firebase/index.d.ts Co-authored-by: Feiyang <[email protected]> * Update .changeset/clean-toes-pump.md Co-authored-by: Feiyang <[email protected]> Co-authored-by: Sam Olsen <[email protected]> Co-authored-by: Yuchen Shi <[email protected]> Co-authored-by: Feiyang <[email protected]>
1 parent 8c0f1e2 commit eeb1dfa

25 files changed

+1539
-133
lines changed

.changeset/clean-toes-pump.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/auth': minor
3+
'firebase': minor
4+
---
5+
6+
Add ability to configure the SDK to communicate with the Firebase Auth emulator.

packages/auth/buildtools/run_demo.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ cp ../firebase/firebase-auth.js demo/public/dist/firebase-auth.js
3434
cp ../firebase/firebase-database.js demo/public/dist/firebase-database.js
3535
# Serve demo app.
3636
cd demo
37-
`yarn bin`/firebase serve --only hosting,functions
37+
`yarn bin`/firebase emulators:start

packages/auth/demo/.gitignore

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
firebase-debug.log*
8+
firebase-debug.*.log*
9+
10+
# Firebase cache
11+
.firebase/
12+
13+
# Firebase config
14+
15+
# Uncomment this if you'd like others to create their own Firebase project.
16+
# For a team working on the same Firebase project(s), it is recommended to leave
17+
# it commented so all members can deploy to the same project(s) in .firebaserc.
18+
# .firebaserc
19+
20+
# Runtime data
21+
pids
22+
*.pid
23+
*.seed
24+
*.pid.lock
25+
26+
# Directory for instrumented libs generated by jscoverage/JSCover
27+
lib-cov
28+
29+
# Coverage directory used by tools like istanbul
30+
coverage
31+
32+
# nyc test coverage
33+
.nyc_output
34+
35+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36+
.grunt
37+
38+
# Bower dependency directory (https://bower.io/)
39+
bower_components
40+
41+
# node-waf configuration
42+
.lock-wscript
43+
44+
# Compiled binary addons (http://nodejs.org/api/addons.html)
45+
build/Release
46+
47+
# Dependency directories
48+
node_modules/
49+
50+
# Optional npm cache directory
51+
.npm
52+
53+
# Optional eslint cache
54+
.eslintcache
55+
56+
# Optional REPL history
57+
.node_repl_history
58+
59+
# Output of 'npm pack'
60+
*.tgz
61+
62+
# Yarn Integrity file
63+
.yarn-integrity
64+
65+
# dotenv environment variables file
66+
.env

packages/auth/demo/firebase.json

+17
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,22 @@
1010
"function": "checkIfAuthenticated"
1111
}
1212
]
13+
},
14+
"emulators": {
15+
"auth": {
16+
"port": 9099
17+
},
18+
"functions": {
19+
"port": 5001
20+
},
21+
"database": {
22+
"port": 9000
23+
},
24+
"hosting": {
25+
"port": 5000
26+
},
27+
"ui": {
28+
"enabled": true
29+
}
1330
}
1431
}

packages/auth/demo/public/sample-config.js

+3
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ var config = {
2121
storageBucket: "your-app.appspot.com",
2222
messagingSenderId: "MESSAGING_SENDER_ID"
2323
};
24+
25+
// Uncomment the following line to use with emulator
26+
// var emulatorUrl = 'http://localhost:9099';

packages/auth/demo/public/script.js

+6
Original file line numberDiff line numberDiff line change
@@ -1623,12 +1623,18 @@ function initApp(){
16231623
log('Initializing app...');
16241624
app = firebase.initializeApp(config);
16251625
auth = app.auth();
1626+
if (window.emulatorUrl) {
1627+
auth.useEmulator(emulatorUrl);
1628+
}
16261629

16271630
tempApp = firebase.initializeApp({
16281631
'apiKey': config['apiKey'],
16291632
'authDomain': config['authDomain']
16301633
}, auth['app']['name'] + '-temp');
16311634
tempAuth = tempApp.auth();
1635+
if (window.emulatorUrl) {
1636+
tempAuth.useEmulator(emulatorUrl);
1637+
}
16321638

16331639
// Listen to reCAPTCHA config togglers.
16341640
initRecaptchaToggle(function(size) {

packages/auth/demo/public/service-worker.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ self.addEventListener('install', function(event) {
9191
});
9292

9393
// As this is a test app, let's only return cached data when offline.
94-
self.addEventListener('fetch', function(event) {
94+
self.addEventListener('fetch', function (event) {
9595
var fetchEvent = event;
9696
var requestProcessor = function(idToken) {
9797
var req = event.request;
@@ -154,7 +154,10 @@ self.addEventListener('fetch', function(event) {
154154
event.respondWith(getIdToken().then(requestProcessor, requestProcessor));
155155
});
156156

157-
self.addEventListener('activate', function(event) {
157+
self.addEventListener('activate', function (event) {
158+
if (window.emulatorUrl) {
159+
firebase.auth().useEmulator(emulatorUrl);
160+
}
158161
// Update this list with all caches that need to remain cached.
159162
var cacheWhitelist = ['cache-v1'];
160163
event.waitUntil(caches.keys().then(function(cacheNames) {

packages/auth/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@firebase/auth-types": "0.10.1"
2626
},
2727
"devDependencies": {
28+
"firebase-tools": "8.11.2",
2829
"google-closure-compiler": "20200112.0.0",
2930
"google-closure-library": "20200224.0.0",
3031
"gulp": "4.0.2",

packages/auth/src/auth.js

+130-5
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@ fireauth.Auth = function(app) {
184184
* is currently only used to log FirebaseUI.
185185
*/
186186
this.frameworks_ = [];
187+
188+
/**
189+
* @private {?fireauth.constants.EmulatorSettings} The current
190+
* emulator settings.
191+
*/
192+
this.emulatorConfig_ = null;
187193
};
188194
goog.inherits(fireauth.Auth, goog.events.EventTarget);
189195

@@ -202,6 +208,20 @@ fireauth.Auth.LanguageCodeChangeEvent = function(languageCode) {
202208
goog.inherits(fireauth.Auth.LanguageCodeChangeEvent, goog.events.Event);
203209

204210

211+
/**
212+
* Emulator config change custom event.
213+
* @param {?fireauth.constants.EmulatorSettings} emulatorConfig The new
214+
* emulator settings.
215+
* @constructor
216+
* @extends {goog.events.Event}
217+
*/
218+
fireauth.Auth.EmulatorConfigChangeEvent = function(emulatorConfig) {
219+
goog.events.Event.call(this, fireauth.constants.AuthEventType.EMULATOR_CONFIG_CHANGED);
220+
this.emulatorConfig = emulatorConfig;
221+
};
222+
goog.inherits(fireauth.Auth.EmulatorConfigChangeEvent, goog.events.Event);
223+
224+
205225
/**
206226
* Framework change custom event.
207227
* @param {!Array<string>} frameworks The new frameworks array.
@@ -272,6 +292,65 @@ fireauth.Auth.prototype.useDeviceLanguage = function() {
272292
};
273293

274294

295+
/**
296+
* Sets the emulator configuration (go/firebase-emulator-connection-api).
297+
* @param {string} url The url for the Auth emulator.
298+
*/
299+
fireauth.Auth.prototype.useEmulator = function(url) {
300+
// Emulator config can only be set once.
301+
if (!this.emulatorConfig_) {
302+
// Emit a warning so dev knows we are now in test mode.
303+
this.emitEmulatorWarning_();
304+
// Persist the config.
305+
this.emulatorConfig_ = { url };
306+
// Disable app verification.
307+
this.settings_().setAppVerificationDisabledForTesting(true);
308+
// Update RPC handler endpoints.
309+
this.rpcHandler_.updateEmulatorConfig(this.emulatorConfig_);
310+
// Notify external event listeners.
311+
this.notifyEmulatorConfigListeners_();
312+
}
313+
}
314+
315+
316+
/**
317+
* Emits a console warning and a visual banner if emulator integration is
318+
* enabled.
319+
*/
320+
fireauth.Auth.prototype.emitEmulatorWarning_ = function() {
321+
fireauth.util.consoleWarn('WARNING: You are using the Auth Emulator,' +
322+
' which is intended for local testing only. Do not use with' +
323+
' production credentials.');
324+
if (goog.global.document) {
325+
fireauth.util.onDomReady().then(() => {
326+
const ele = goog.global.document.createElement('div');
327+
ele.innerText = 'Running in emulator mode. Do not use with production' +
328+
' credentials.';
329+
ele.style.position = 'fixed';
330+
ele.style.width = '100%';
331+
ele.style.backgroundColor = '#ffffff';
332+
ele.style.border = '.1em solid #000000';
333+
ele.style.color = '#ff0000';
334+
ele.style.bottom = '0px';
335+
ele.style.left = '0px';
336+
ele.style.margin = '0px';
337+
ele.style.zIndex = 10000;
338+
ele.style.textAlign = 'center';
339+
ele.classList.add('firebase-emulator-warning');
340+
goog.global.document.body.appendChild(ele);
341+
});
342+
}
343+
}
344+
345+
346+
/**
347+
* @return {?fireauth.constants.EmulatorSettings}
348+
*/
349+
fireauth.Auth.prototype.getEmulatorConfig = function() {
350+
return this.emulatorConfig_;
351+
}
352+
353+
275354
/**
276355
* @param {string} frameworkId The framework identifier.
277356
*/
@@ -396,7 +475,15 @@ fireauth.Auth.prototype.notifyLanguageCodeListeners_ = function() {
396475
};
397476

398477

399-
478+
/**
479+
* Notifies all external listeners of the emulator config change.
480+
* @private
481+
*/
482+
fireauth.Auth.prototype.notifyEmulatorConfigListeners_ = function() {
483+
// Notify external listeners on the emulator config change.
484+
this.dispatchEvent(
485+
new fireauth.Auth.EmulatorConfigChangeEvent(this.emulatorConfig_));
486+
}
400487

401488

402489
/**
@@ -449,7 +536,10 @@ fireauth.Auth.prototype.initAuthEventManager_ = function() {
449536
// By this time currentUser should be ready if available and will be able
450537
// to resolve linkWithRedirect if detected.
451538
self.authEventManager_ = fireauth.AuthEventManager.getManager(
452-
authDomain, apiKey, self.app_().name);
539+
authDomain,
540+
apiKey,
541+
self.app_().name,
542+
self.emulatorConfig_);
453543
// Subscribe Auth instance.
454544
self.authEventManager_.subscribe(self);
455545
// Subscribe current user by enabling popup and redirect on that user.
@@ -471,7 +561,10 @@ fireauth.Auth.prototype.initAuthEventManager_ = function() {
471561
/** @type {!fireauth.AuthUser} */ (self.redirectUser_));
472562
// Set the user Firebase frameworks for the redirect user.
473563
self.setUserFramework_(
474-
/** @type {!fireauth.AuthUser} */ (self.redirectUser_));
564+
/** @type {!fireauth.AuthUser} */(self.redirectUser_));
565+
// Set the user Emulator configuration for the redirect user.
566+
self.setUserEmulatorConfig_(
567+
/** @type {!fireauth.AuthUser} */(self.redirectUser_));
475568
// Reference to redirect user no longer needed.
476569
self.redirectUser_ = null;
477570
}
@@ -650,7 +743,8 @@ fireauth.Auth.prototype.signInWithPopup = function(provider) {
650743
firebase.SDK_VERSION || null,
651744
null,
652745
null,
653-
this.getTenantId());
746+
this.getTenantId(),
747+
this.emulatorConfig_);
654748
}
655749
// The popup must have a name, otherwise when successive popups are triggered
656750
// they will all render in the same instance and none will succeed since the
@@ -856,6 +950,9 @@ fireauth.Auth.prototype.signInWithIdTokenResponse =
856950
options['apiKey'] = self.app_().options['apiKey'];
857951
options['authDomain'] = self.app_().options['authDomain'];
858952
options['appName'] = self.app_().name;
953+
if (self.emulatorConfig_) {
954+
options['emulatorConfig'] = self.emulatorConfig_;
955+
}
859956
// Wait for state to be ready.
860957
// This is used internally and is also used for redirect sign in so there is
861958
// no need for waiting for redirect result to resolve since redirect result
@@ -911,6 +1008,9 @@ fireauth.Auth.prototype.setCurrentUser_ = function(user) {
9111008
// Set the current frameworks used on the user and set current Auth instance
9121009
// as the framework change dispatcher.
9131010
this.setUserFramework_(user);
1011+
// If a user is available, set the emulator config on it and set current
1012+
// Auth instance as emulator config change dispatcher.
1013+
this.setUserEmulatorConfig_(user);
9141014
}
9151015
};
9161016

@@ -1001,7 +1101,7 @@ fireauth.Auth.prototype.initAuthState_ = function() {
10011101
var p = this.initRedirectUser_().then(function() {
10021102
// Override user's authDomain with app's authDomain if there is a mismatch.
10031103
return /** @type {!fireauth.storage.UserManager} */ (
1004-
self.userStorageManager_).getCurrentUser(authDomain);
1104+
self.userStorageManager_).getCurrentUser(authDomain, self.emulatorConfig_);
10051105
}).then(function(user) {
10061106
// Logged in user.
10071107
if (user) {
@@ -1179,6 +1279,22 @@ fireauth.Auth.prototype.setUserLanguage_ = function(user) {
11791279
};
11801280

11811281

1282+
/**
1283+
* Updates the emulator config on the provided user and configures the user
1284+
* to listen to the Auth instance for any emulator config changes.
1285+
* @param {!fireauth.AuthUser} user The user to whose emulator config needs
1286+
* to be set.
1287+
* @private
1288+
*/
1289+
fireauth.Auth.prototype.setUserEmulatorConfig_ = function(user) {
1290+
// Sets the current emulator config on the user.
1291+
user.setEmulatorConfig(this.emulatorConfig_);
1292+
// Sets current Auth instance as emulator config change dispatcher on the
1293+
// user.
1294+
user.setEmulatorConfigChangeDispatcher(this);
1295+
}
1296+
1297+
11821298
/**
11831299
* Handles user state changes.
11841300
* @param {!fireauth.AuthUser} user The user which triggered the state changes.
@@ -1679,6 +1795,15 @@ fireauth.Auth.prototype.app_ = function() {
16791795
};
16801796

16811797

1798+
/**
1799+
* @return {!fireauth.AuthSettings} The settings for this Auth object.
1800+
* @private
1801+
*/
1802+
fireauth.Auth.prototype.settings_ = function () {
1803+
return this['settings'];
1804+
};
1805+
1806+
16821807
/**
16831808
* @return {!fireauth.RpcHandler} The RPC handler.
16841809
*/

0 commit comments

Comments
 (0)