Skip to content
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

Add a way to specify emulator host/port for Auth #3810

Merged
merged 11 commits into from Oct 13, 2020
5 changes: 5 additions & 0 deletions .changeset/clean-toes-pump.md
@@ -0,0 +1,5 @@
---
'@firebase/auth': minor
avolkovi marked this conversation as resolved.
Show resolved Hide resolved
---

Add ability to configure the SDK to communicate with the Firebase Auth emulator.
2 changes: 1 addition & 1 deletion packages/auth/buildtools/run_demo.sh
Expand Up @@ -34,4 +34,4 @@ cp ../firebase/firebase-auth.js demo/public/dist/firebase-auth.js
cp ../firebase/firebase-database.js demo/public/dist/firebase-database.js
# Serve demo app.
cd demo
`yarn bin`/firebase serve --only hosting,functions
`yarn bin`/firebase emulators:start
66 changes: 66 additions & 0 deletions packages/auth/demo/.gitignore
@@ -0,0 +1,66 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
firebase-debug.*.log*

# Firebase cache
.firebase/

# Firebase config

# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
17 changes: 17 additions & 0 deletions packages/auth/demo/firebase.json
Expand Up @@ -10,5 +10,22 @@
"function": "checkIfAuthenticated"
}
]
},
"emulators": {
"auth": {
"port": 9099
},
"functions": {
"port": 5001
},
"database": {
"port": 9000
},
"hosting": {
"port": 5000
},
"ui": {
"enabled": true
}
}
}
3 changes: 3 additions & 0 deletions packages/auth/demo/public/sample-config.js
Expand Up @@ -21,3 +21,6 @@ var config = {
storageBucket: "your-app.appspot.com",
messagingSenderId: "MESSAGING_SENDER_ID"
};

// Uncomment the following line to use with emulator
// var emulatorUrl = 'http://localhost:9099';
6 changes: 6 additions & 0 deletions packages/auth/demo/public/script.js
Expand Up @@ -1623,12 +1623,18 @@ function initApp(){
log('Initializing app...');
app = firebase.initializeApp(config);
auth = app.auth();
if (window.emulatorUrl) {
auth.useEmulator(emulatorUrl);
}

tempApp = firebase.initializeApp({
'apiKey': config['apiKey'],
'authDomain': config['authDomain']
}, auth['app']['name'] + '-temp');
tempAuth = tempApp.auth();
if (window.emulatorUrl) {
tempAuth.useEmulator(emulatorUrl);
}

// Listen to reCAPTCHA config togglers.
initRecaptchaToggle(function(size) {
Expand Down
7 changes: 5 additions & 2 deletions packages/auth/demo/public/service-worker.js
Expand Up @@ -91,7 +91,7 @@ self.addEventListener('install', function(event) {
});

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

self.addEventListener('activate', function(event) {
self.addEventListener('activate', function (event) {
if (window.emulatorUrl) {
firebase.auth().useEmulator(emulatorUrl);
}
// Update this list with all caches that need to remain cached.
var cacheWhitelist = ['cache-v1'];
event.waitUntil(caches.keys().then(function(cacheNames) {
Expand Down
1 change: 1 addition & 0 deletions packages/auth/package.json
Expand Up @@ -25,6 +25,7 @@
"@firebase/auth-types": "0.10.1"
},
"devDependencies": {
"firebase-tools": "8.11.2",
"google-closure-compiler": "20200112.0.0",
"google-closure-library": "20200224.0.0",
"gulp": "4.0.2",
Expand Down
135 changes: 130 additions & 5 deletions packages/auth/src/auth.js
Expand Up @@ -184,6 +184,12 @@ fireauth.Auth = function(app) {
* is currently only used to log FirebaseUI.
*/
this.frameworks_ = [];

/**
* @private {?fireauth.constants.EmulatorSettings} The current
* emulator settings.
*/
this.emulatorConfig_ = null;
};
goog.inherits(fireauth.Auth, goog.events.EventTarget);

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


/**
* Emulator config change custom event.
* @param {?fireauth.constants.EmulatorSettings} emulatorConfig The new
* emulator settings.
* @constructor
* @extends {goog.events.Event}
*/
fireauth.Auth.EmulatorConfigChangeEvent = function(emulatorConfig) {
goog.events.Event.call(this, fireauth.constants.AuthEventType.EMULATOR_CONFIG_CHANGED);
this.emulatorConfig = emulatorConfig;
};
goog.inherits(fireauth.Auth.EmulatorConfigChangeEvent, goog.events.Event);


/**
* Framework change custom event.
* @param {!Array<string>} frameworks The new frameworks array.
Expand Down Expand Up @@ -272,6 +292,65 @@ fireauth.Auth.prototype.useDeviceLanguage = function() {
};


/**
* Sets the emulator configuration (go/firebase-emulator-connection-api).
* @param {string} url The url for the Auth emulator.
*/
fireauth.Auth.prototype.useEmulator = function(url) {
// Emulator config can only be set once.
if (!this.emulatorConfig_) {
// Emit a warning so dev knows we are now in test mode.
this.emitEmulatorWarning_();
// Persist the config.
this.emulatorConfig_ = { url };
// Disable app verification.
this.settings_().setAppVerificationDisabledForTesting(true);
// Update RPC handler endpoints.
this.rpcHandler_.updateEmulatorConfig(this.emulatorConfig_);
// Notify external event listeners.
this.notifyEmulatorConfigListeners_();
}
}


/**
* Emits a console warning and a visual banner if emulator integration is
* enabled.
*/
fireauth.Auth.prototype.emitEmulatorWarning_ = function() {
fireauth.util.consoleWarn('WARNING: You are using the Auth Emulator,' +
' which is intended for local testing only. Do not use with' +
' production credentials.');
if (goog.global.document) {
yuchenshi marked this conversation as resolved.
Show resolved Hide resolved
fireauth.util.onDomReady().then(() => {
const ele = goog.global.document.createElement('div');
ele.innerText = 'Running in emulator mode. Do not use with production' +
' credentials.';
ele.style.position = 'fixed';
ele.style.width = '100%';
ele.style.backgroundColor = '#ffffff';
ele.style.border = '.1em solid #000000';
ele.style.color = '#ff0000';
ele.style.bottom = '0px';
ele.style.left = '0px';
ele.style.margin = '0px';
ele.style.zIndex = 10000;
ele.style.textAlign = 'center';
ele.classList.add('firebase-emulator-warning');
goog.global.document.body.appendChild(ele);
});
}
}


/**
* @return {?fireauth.constants.EmulatorSettings}
*/
fireauth.Auth.prototype.getEmulatorConfig = function() {
return this.emulatorConfig_;
}


/**
* @param {string} frameworkId The framework identifier.
*/
Expand Down Expand Up @@ -396,7 +475,15 @@ fireauth.Auth.prototype.notifyLanguageCodeListeners_ = function() {
};



/**
* Notifies all external listeners of the emulator config change.
* @private
*/
fireauth.Auth.prototype.notifyEmulatorConfigListeners_ = function() {
// Notify external listeners on the emulator config change.
this.dispatchEvent(
new fireauth.Auth.EmulatorConfigChangeEvent(this.emulatorConfig_));
}


/**
Expand Down Expand Up @@ -449,7 +536,10 @@ fireauth.Auth.prototype.initAuthEventManager_ = function() {
// By this time currentUser should be ready if available and will be able
// to resolve linkWithRedirect if detected.
self.authEventManager_ = fireauth.AuthEventManager.getManager(
authDomain, apiKey, self.app_().name);
authDomain,
apiKey,
self.app_().name,
self.emulatorConfig_);
// Subscribe Auth instance.
self.authEventManager_.subscribe(self);
// Subscribe current user by enabling popup and redirect on that user.
Expand All @@ -471,7 +561,10 @@ fireauth.Auth.prototype.initAuthEventManager_ = function() {
/** @type {!fireauth.AuthUser} */ (self.redirectUser_));
// Set the user Firebase frameworks for the redirect user.
self.setUserFramework_(
/** @type {!fireauth.AuthUser} */ (self.redirectUser_));
/** @type {!fireauth.AuthUser} */(self.redirectUser_));
// Set the user Emulator configuration for the redirect user.
self.setUserEmulatorConfig_(
/** @type {!fireauth.AuthUser} */(self.redirectUser_));
// Reference to redirect user no longer needed.
self.redirectUser_ = null;
}
Expand Down Expand Up @@ -650,7 +743,8 @@ fireauth.Auth.prototype.signInWithPopup = function(provider) {
firebase.SDK_VERSION || null,
null,
null,
this.getTenantId());
this.getTenantId(),
this.emulatorConfig_);
}
// The popup must have a name, otherwise when successive popups are triggered
// they will all render in the same instance and none will succeed since the
Expand Down Expand Up @@ -856,6 +950,9 @@ fireauth.Auth.prototype.signInWithIdTokenResponse =
options['apiKey'] = self.app_().options['apiKey'];
options['authDomain'] = self.app_().options['authDomain'];
options['appName'] = self.app_().name;
if (self.emulatorConfig_) {
options['emulatorConfig'] = self.emulatorConfig_;
}
// Wait for state to be ready.
// This is used internally and is also used for redirect sign in so there is
// no need for waiting for redirect result to resolve since redirect result
Expand Down Expand Up @@ -911,6 +1008,9 @@ fireauth.Auth.prototype.setCurrentUser_ = function(user) {
// Set the current frameworks used on the user and set current Auth instance
// as the framework change dispatcher.
this.setUserFramework_(user);
// If a user is available, set the emulator config on it and set current
// Auth instance as emulator config change dispatcher.
this.setUserEmulatorConfig_(user);
}
};

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


/**
* Updates the emulator config on the provided user and configures the user
* to listen to the Auth instance for any emulator config changes.
* @param {!fireauth.AuthUser} user The user to whose emulator config needs
* to be set.
* @private
*/
fireauth.Auth.prototype.setUserEmulatorConfig_ = function(user) {
// Sets the current emulator config on the user.
user.setEmulatorConfig(this.emulatorConfig_);
// Sets current Auth instance as emulator config change dispatcher on the
// user.
user.setEmulatorConfigChangeDispatcher(this);
}


/**
* Handles user state changes.
* @param {!fireauth.AuthUser} user The user which triggered the state changes.
Expand Down Expand Up @@ -1679,6 +1795,15 @@ fireauth.Auth.prototype.app_ = function() {
};


/**
* @return {!fireauth.AuthSettings} The settings for this Auth object.
* @private
*/
fireauth.Auth.prototype.settings_ = function () {
return this['settings'];
};


/**
* @return {!fireauth.RpcHandler} The RPC handler.
*/
Expand Down