diff --git a/.changeset/wet-badgers-nail.md b/.changeset/wet-badgers-nail.md
new file mode 100644
index 00000000000..47182420bb9
--- /dev/null
+++ b/.changeset/wet-badgers-nail.md
@@ -0,0 +1,6 @@
+---
+"@firebase/auth": patch
+"firebase": patch
+---
+
+Add the `useEmulator()` function and `emulatorConfig` to the `firebase` package externs
diff --git a/packages/auth/src/auth.js b/packages/auth/src/auth.js
index 8bb60114736..e2863a8dd1e 100644
--- a/packages/auth/src/auth.js
+++ b/packages/auth/src/auth.js
@@ -309,7 +309,7 @@ fireauth.Auth.prototype.useEmulator = function(url, options) {
const disableBanner = options ? !!options['disableWarnings'] : false;
this.emitEmulatorWarning_(disableBanner);
// Persist the config.
- this.emulatorConfig_ = { url };
+ this.emulatorConfig_ = {url, disableWarnings: disableBanner};
// Disable app verification.
this.settings_().setAppVerificationDisabledForTesting(true);
// Update RPC handler endpoints.
@@ -353,10 +353,22 @@ fireauth.Auth.prototype.emitEmulatorWarning_ = function(disableBanner) {
/**
- * @return {?fireauth.constants.EmulatorSettings}
+ * @return {?fireauth.constants.EmulatorConfig}
*/
fireauth.Auth.prototype.getEmulatorConfig = function() {
- return this.emulatorConfig_;
+ if (!this.emulatorConfig_) {
+ return null;
+ }
+ const uri = goog.Uri.parse(this.emulatorConfig_.url);
+ return /** @type {!fireauth.constants.EmulatorConfig} */ (
+ fireauth.object.makeReadonlyCopy({
+ 'protocol': uri.getScheme(),
+ 'host': uri.getDomain(),
+ 'port': uri.getPort(),
+ 'options': fireauth.object.makeReadonlyCopy({
+ 'disableWarnings': this.emulatorConfig_.disableWarnings,
+ }),
+ }));
}
@@ -470,6 +482,19 @@ fireauth.Auth.prototype.initializeReadableWritableProps_ = function() {
// Initialize to null.
/** @private {?string} The current Auth instance's tenant ID. */
this.tenantId_ = null;
+
+ // Add the emulator configuration property (readonly).
+ Object.defineProperty(/** @type {!Object} */ (this), 'emulatorConfig', {
+ /**
+ * @this {!Object}
+ * @return {?fireauth.constants.EmulatorConfig} The emulator config if
+ * enabled.
+ */
+ get: function() {
+ return this.getEmulatorConfig();
+ },
+ enumerable: false
+ });
};
diff --git a/packages/auth/src/defines.js b/packages/auth/src/defines.js
index be41f997312..a2b123904aa 100644
--- a/packages/auth/src/defines.js
+++ b/packages/auth/src/defines.js
@@ -173,9 +173,33 @@ fireauth.constants.OIDC_PREFIX = 'oidc.';
* The settings of an Auth emulator. The fields are:
*
* - url: defines the URL where the emulator is running.
+ * - disableWarnings: if true, banner is not shown on the page.
*
* @typedef {{
* url: string,
+ * disableWarnings: boolean,
* }}
*/
-fireauth.constants.EmulatorSettings;
\ No newline at end of file
+fireauth.constants.EmulatorSettings;
+
+
+/**
+ * The (externally visible) emulator configuration, used for
+ * getEmulatorConfig(). The fields are:
+ *
+ * - protocol: the protocol used by the emulator (http or https).
+ * - host: the host used to reach the emulator.
+ * - port: the port used to reach the emulator.
+ * - options: a list of options used to configure the SDK's interaction with
+ * the emulator.
+ *
+ * @typedef {{
+ * protocol: string,
+ * host: string,
+ * port: (number|null),
+ * options: {
+ * disableWarnings: boolean,
+ * }
+ * }}
+ */
+fireauth.constants.EmulatorConfig;
\ No newline at end of file
diff --git a/packages/auth/src/exports_auth.js b/packages/auth/src/exports_auth.js
index 37f23211720..fbbf123818e 100644
--- a/packages/auth/src/exports_auth.js
+++ b/packages/auth/src/exports_auth.js
@@ -225,7 +225,9 @@ fireauth.exportlib.exportPrototypeProperties(
fireauth.args.string(),
fireauth.args.null(),
'tenantId')
- }
+ },
+ // emulatorConfig is omitted here as it is readonly and therefore does not
+ // need argument validation.
});
// Exports firebase.auth.Auth.Persistence.
diff --git a/packages/auth/test/auth_test.js b/packages/auth/test/auth_test.js
index 701df7fcc3b..ab32b990184 100644
--- a/packages/auth/test/auth_test.js
+++ b/packages/auth/test/auth_test.js
@@ -952,15 +952,19 @@ function testUseEmulator() {
auth1.useEmulator('http://emulator.test.domain:1234');
assertObjectEquals(
{
- url: 'http://emulator.test.domain:1234',
+ protocol: 'http',
+ host: 'emulator.test.domain',
+ port: 1234,
+ options: {disableWarnings: false},
},
- auth1.getEmulatorConfig());
+ auth1.emulatorConfig);
// Should notify the RPC handler.
assertEquals(
1, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
assertObjectEquals(
{
url: 'http://emulator.test.domain:1234',
+ disableWarnings: false,
},
fireauth.RpcHandler.prototype.updateEmulatorConfig.getLastCall()
.getArgument(0)
@@ -986,9 +990,12 @@ function testUseEmulator() {
auth1.useEmulator('http://emulator.test.domain:1234');
assertObjectEquals(
{
- url: 'http://emulator.test.domain:1234',
+ protocol: 'http',
+ host: 'emulator.test.domain',
+ port: 1234,
+ options: {disableWarnings: false},
},
- auth1.getEmulatorConfig());
+ auth1.emulatorConfig);
assertEquals(
1, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
assertEquals(1, fireauth.util.consoleInfo.getCallCount());
@@ -997,9 +1004,12 @@ function testUseEmulator() {
auth1.useEmulator('http://emulator.other.domain:9876');
assertObjectEquals(
{
- url: 'http://emulator.test.domain:1234',
+ protocol: 'http',
+ host: 'emulator.test.domain',
+ port: 1234,
+ options: {disableWarnings: false},
},
- auth1.getEmulatorConfig());
+ auth1.emulatorConfig);
assertEquals(
1, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
}
@@ -1030,16 +1040,20 @@ function testUseEmulator_withDisableWarnings() {
auth1.useEmulator(
'http://emulator.test.domain:1234', {disableWarnings: true});
assertObjectEquals(
- {
- url: 'http://emulator.test.domain:1234',
- },
- auth1.getEmulatorConfig());
+ {
+ protocol: 'http',
+ host: 'emulator.test.domain',
+ port: 1234,
+ options: {disableWarnings: true},
+ },
+ auth1.emulatorConfig);
// Should notify the RPC handler.
assertEquals(
1, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
assertObjectEquals(
{
url: 'http://emulator.test.domain:1234',
+ disableWarnings: true,
},
fireauth.RpcHandler.prototype.updateEmulatorConfig.getLastCall()
.getArgument(0));
@@ -1057,6 +1071,76 @@ function testUseEmulator_withDisableWarnings() {
}
+function testEmulatorConfig() {
+ app1 = firebase.initializeApp(config1, appId1);
+ auth1 = app1.auth();
+
+ // Update the emulator config.
+ auth1.useEmulator(
+ 'http://emulator.test.domain:1234', {disableWarnings: true});
+ assertObjectEquals(
+ {
+ protocol: 'http',
+ host: 'emulator.test.domain',
+ port: 1234,
+ options: {disableWarnings: true},
+ },
+ auth1.emulatorConfig);
+}
+
+
+/**
+ * Asserts that the port is correctly set to null if no port supplied.
+ */
+function testEmulatorConfig_noPortSpecified() {
+ app1 = firebase.initializeApp(config1, appId1);
+ auth1 = app1.auth();
+
+ // Update the emulator config.
+ auth1.useEmulator('http://emulator.test.domain');
+ assertObjectEquals(
+ {
+ protocol: 'http',
+ host: 'emulator.test.domain',
+ port: null,
+ options: {disableWarnings: false},
+ },
+ auth1.emulatorConfig);
+}
+
+
+/**
+ * Asserts that the port is correctly assigned 0 if specifically set to 0 for
+ * some reason. Also checks https protocol.
+ */
+function testEmulatorConfig_portZeroAndHttpsSpecified() {
+ app1 = firebase.initializeApp(config1, appId1);
+ auth1 = app1.auth();
+
+ // Update the emulator config.
+ auth1.useEmulator('https://emulator.test.domain:0');
+ assertObjectEquals(
+ {
+ protocol: 'https',
+ host: 'emulator.test.domain',
+ port: 0,
+ options: {disableWarnings: false},
+ },
+ auth1.emulatorConfig);
+}
+
+
+/**
+ * Asserts that the function returns null if useEmulator is not called.
+ */
+function testEmulatorConfig_nullIfNoEmulatorConfig() {
+ app1 = firebase.initializeApp(config1, appId1);
+ auth1 = app1.auth();
+
+ assertNull(auth1.emulatorConfig);
+}
+
+
function testGetSetTenantId() {
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
@@ -2317,59 +2401,6 @@ function testAuth_authEventManager() {
}
-function testAuth_authEventManager_withEmulator() {
- // Test Auth event manager.
- fireauth.AuthEventManager.ENABLED = true;
- stubs.reset();
- initializeMockStorage();
- var expectedManager = {
- 'subscribe': goog.testing.recordFunction(),
- 'unsubscribe': goog.testing.recordFunction(),
- 'clearRedirectResult': goog.testing.recordFunction()
- };
- // Return stub manager.
- stubs.replace(
- fireauth.AuthEventManager,
- 'getManager',
- function (authDomain, apiKey, appName, emulatorConfig) {
- assertEquals('subdomain.firebaseapp.com', authDomain);
- assertEquals('API_KEY', apiKey);
- assertEquals(appId1, appName);
- assertObjectEquals(emulatorConfig, {
- url: 'http://emulator.test.domain:1234'
- });
- return expectedManager;
- });
- asyncTestCase.waitForSignals(1);
- app1 = firebase.initializeApp(config3, appId1);
- auth1 = app1.auth();
- auth1.useEmulator('http://emulator.test.domain:1234');
- // Test manager initialized and Auth subscribed.
- auth1.onIdTokenChanged(function (user) {
- var manager = fireauth.AuthEventManager.getManager(
- config3['authDomain'], config3['apiKey'], app1.name, {
- url: 'http://emulator.test.domain:1234',
- });
- assertEquals(expectedManager, manager);
- assertEquals(0, expectedManager.unsubscribe.getCallCount());
- assertEquals(1, expectedManager.subscribe.getCallCount());
- assertEquals(
- auth1, expectedManager.subscribe.getLastCall().getArgument(0));
- assertEquals(0, expectedManager.clearRedirectResult.getCallCount());
- // Delete should trigger unsubscribe and redirect result clearing.
- auth1.delete();
- // After destroy, Auth should be unsubscribed.
- assertEquals(1, expectedManager.subscribe.getCallCount());
- assertEquals(1, expectedManager.unsubscribe.getCallCount());
- // Redirect result should also be cleared.
- assertEquals(1, expectedManager.clearRedirectResult.getCallCount());
- assertEquals(
- auth1, expectedManager.unsubscribe.getLastCall().getArgument(0));
- asyncTestCase.signal();
- });
-}
-
-
/** Asserts that AuthEventManager can pass through emulator settings. */
function testAuth_authEventManager_withEmulator() {
// Test Auth event manager.
@@ -2390,7 +2421,8 @@ function testAuth_authEventManager_withEmulator() {
assertEquals('API_KEY', apiKey);
assertEquals(appId1, appName);
assertObjectEquals(emulatorConfig, {
- url: 'http://emulator.host:1234'
+ url: 'http://emulator.host:1234',
+ disableWarnings: false,
});
return expectedManager;
});
@@ -2402,7 +2434,8 @@ function testAuth_authEventManager_withEmulator() {
auth1.onIdTokenChanged(function (user) {
var manager = fireauth.AuthEventManager.getManager(
config3['authDomain'], config3['apiKey'], app1.name, {
- url: 'http://emulator.host:1234'
+ url: 'http://emulator.host:1234',
+ disableWarnings: false,
});
assertEquals(expectedManager, manager);
assertEquals(0, expectedManager.unsubscribe.getCallCount());
@@ -2667,7 +2700,8 @@ function testAuth_initState_signedInStatus_withEmulator() {
.getThis());
assertObjectEquals(
{
- url: 'http://emulator.test.domain:1234'
+ url: 'http://emulator.test.domain:1234',
+ disableWarnings: false,
},
fireauth.RpcHandler.prototype.updateEmulatorConfig.getLastCall()
.getArgument(0));
@@ -4526,6 +4560,7 @@ function testAuth_signInWithIdTokenResponse_withEmulator() {
var expectedOptions = Object.assign({}, config3);
expectedOptions['emulatorConfig'] = {
url: 'http://emulator.test.domain:1234',
+ disableWarnings: false,
};
// The newly signed in user.
var user1 = new fireauth.AuthUser(
diff --git a/packages/firebase/externs/firebase-auth-externs.js b/packages/firebase/externs/firebase-auth-externs.js
index c588ea5e2b4..f286f66cf64 100644
--- a/packages/firebase/externs/firebase-auth-externs.js
+++ b/packages/firebase/externs/firebase-auth-externs.js
@@ -1218,6 +1218,47 @@ firebase.auth.Auth.prototype.app;
*/
firebase.auth.Auth.prototype.currentUser;
+/**
+ * The full emulator configuration as set on `auth().emulatorConfig`.
+ *
+ * - protocol: the protocol used by the emulator (http or https).
+ * - host: the host used to reach the emulator.
+ * - port: the port used to reach the emulator.
+ * - options: a list of options used to configure the SDK's interaction with
+ * the emulator.
+ *
+ *
+ * @typedef {{
+ * protocol: string,
+ * host: string,
+ * port: (number|null),
+ * options: {
+ * disableWarnings: boolean,
+ * }
+ * }}
+ */
+firebase.auth.EmulatorConfig;
+
+/**
+ * The current emulator configuration, or null if not set.
+ *
+ * @type {firebase.auth.EmulatorConfig|null}
+ */
+firebase.auth.Auth.prototype.emulatorConfig;
+
+/**
+ * Configures the SDK to communicate with the Firebase Auth emulator.
+ *
+ * This must be called before any other Auth SDK actions are taken.
+ *
+ * Options can include `disableWarnings`. When set to true, the SDK will not
+ * display a warning banner at the bottom of the page.
+ *
+ * @param {string} url The full emulator url including scheme and port.
+ * @param {!Object=} options Options for configuring the SDK's emulator config.
+ */
+firebase.auth.Auth.prototype.useEmulator = function (url, options) {};
+
/**
* The current Auth instance's tenant ID. This is a readable/writable
* property. When you set the tenant ID of an Auth instance, all future