Skip to content

Expose emulator config for the Auth SDK #4280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/wet-badgers-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@firebase/auth": patch
"firebase": patch
---

Add the `useEmulator()` function and `emulatorConfig` to the `firebase` package externs
31 changes: 28 additions & 3 deletions packages/auth/src/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
}),
}));
}


Expand Down Expand Up @@ -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
});
};


Expand Down
26 changes: 25 additions & 1 deletion packages/auth/src/defines.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,33 @@ fireauth.constants.OIDC_PREFIX = 'oidc.';
* The settings of an Auth emulator. The fields are:
* <ul>
* <li>url: defines the URL where the emulator is running.</li>
* <li>disableWarnings: if true, banner is not shown on the page.</li>
* </ul>
* @typedef {{
* url: string,
* disableWarnings: boolean,
* }}
*/
fireauth.constants.EmulatorSettings;
fireauth.constants.EmulatorSettings;


/**
* The (externally visible) emulator configuration, used for
* getEmulatorConfig(). The fields are:
* <ul>
* <li>protocol: the protocol used by the emulator (http or https).</li>
* <li>host: the host used to reach the emulator.</li>
* <li>port: the port used to reach the emulator.</li>
* <li>options: a list of options used to configure the SDK's interaction with
* the emulator.</li>
* </ul>
* @typedef {{
* protocol: string,
* host: string,
* port: (number|null),
* options: {
* disableWarnings: boolean,
* }
* }}
*/
fireauth.constants.EmulatorConfig;
4 changes: 3 additions & 1 deletion packages/auth/src/exports_auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
167 changes: 101 additions & 66 deletions packages/auth/test/auth_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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());
Expand All @@ -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());
}
Expand Down Expand Up @@ -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));
Expand All @@ -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();
Expand Down Expand Up @@ -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.
Expand All @@ -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;
});
Expand All @@ -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());
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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(
Expand Down
Loading