UNPKG

voluptasmollitia

Version:
1,080 lines (988 loc) 35.5 kB
/** * @license * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @fileoverview Defines fireauth.iframeclient.IfcHandler used to communicate * with the serverless widget. */ goog.provide('fireauth.iframeclient.IfcHandler'); goog.provide('fireauth.iframeclient.IframeUrlBuilder'); goog.provide('fireauth.iframeclient.OAuthUrlBuilder'); goog.require('fireauth.AuthError'); goog.require('fireauth.AuthEvent'); goog.require('fireauth.AuthProvider'); goog.require('fireauth.InvalidOriginError'); goog.require('fireauth.OAuthSignInHandler'); goog.require('fireauth.RpcHandler'); goog.require('fireauth.authenum.Error'); goog.require('fireauth.constants'); goog.require('fireauth.iframeclient.IframeWrapper'); goog.require('fireauth.util'); goog.require('goog.Promise'); goog.require('goog.Timer'); goog.require('goog.Uri'); goog.require('goog.array'); goog.require('goog.object'); /** * The OAuth handler and iframe prototcol. * @const {string} * @suppress {const|duplicate} */ fireauth.iframeclient.SCHEME = 'https'; /** * The OAuth handler and iframe port number. * @const {?number} * @suppress {const|duplicate} */ fireauth.iframeclient.PORT_NUMBER = null; /** * The iframe URL builder used to build the iframe widget URL. * @param {string} authDomain The application authDomain. * @param {string} apiKey The API key. * @param {string} appName The App name. * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator * configuration. * @constructor */ fireauth.iframeclient.IframeUrlBuilder = function(authDomain, apiKey, appName, emulatorConfig) { /** @private {string} The application authDomain. */ this.authDomain_ = authDomain; /** @private {string} The API key. */ this.apiKey_ = apiKey; /** @private {string} The App name. */ this.appName_ = appName; /** * @private @const {?fireauth.constants.EmulatorSettings|undefined} * The emulator configuration. */ this.emulatorConfig_ = emulatorConfig; /** @private {?string|undefined} The client version. */ this.v_ = null; let uri; if (this.emulatorConfig_) { const emulatorUri = goog.Uri.parse(this.emulatorConfig_.url); uri = goog.Uri.create( emulatorUri.getScheme(), null, emulatorUri.getDomain(), emulatorUri.getPort(), '/emulator/auth/iframe', null, null); } else { uri = goog.Uri.create( fireauth.iframeclient.SCHEME, null, this.authDomain_, fireauth.iframeclient.PORT_NUMBER, '/__/auth/iframe', null, null); } /** * @private @const {!goog.Uri} The URI object used to build the iframe URL. */ this.uri_ = uri; this.uri_.setParameterValue('apiKey', this.apiKey_); this.uri_.setParameterValue('appName', this.appName_); /** @private {?string|undefined} The endpoint ID. */ this.endpointId_ = null; /** @private {!Array<string>} The list of framework IDs. */ this.frameworks_ = []; }; /** * Sets the client version. * @param {?string|undefined} v The client version. * @return {!fireauth.iframeclient.IframeUrlBuilder} The current iframe URL * builder instance. */ fireauth.iframeclient.IframeUrlBuilder.prototype.setVersion = function(v) { this.v_ = v; return this; }; /** * Sets the endpoint ID. * @param {?string|undefined} eid The endpoint ID (staging, test Gaia, etc). * @return {!fireauth.iframeclient.IframeUrlBuilder} The current iframe URL * builder instance. */ fireauth.iframeclient.IframeUrlBuilder.prototype.setEndpointId = function(eid) { this.endpointId_ = eid; return this; }; /** * Sets the list of frameworks to pass to the iframe. * @param {?Array<string>|undefined} frameworks The list of frameworks to log. * @return {!fireauth.iframeclient.IframeUrlBuilder} The current iframe URL * builder instance. */ fireauth.iframeclient.IframeUrlBuilder.prototype.setFrameworks = function(frameworks) { this.frameworks_ = goog.array.clone(frameworks || []); return this; }; /** * Modifes the URI with the relevant Auth provider parameters. * @return {string} The constructed OAuth URL string. * @override */ fireauth.iframeclient.IframeUrlBuilder.prototype.toString = function() { // Pass the client version if available. if (this.v_) { this.uri_.setParameterValue('v', this.v_); } else { this.uri_.removeParameter('v'); } // Pass the endpoint ID if available. if (this.endpointId_) { this.uri_.setParameterValue('eid', this.endpointId_); } else { this.uri_.removeParameter('eid'); } // Pass the list of frameworks if available. if (this.frameworks_.length) { this.uri_.setParameterValue('fw', this.frameworks_.join(',')); } else { this.uri_.removeParameter('fw'); } return this.uri_.toString(); }; /** * The OAuth URL builder used to build the OAuth handler widget URL. * @param {string} authDomain The application authDomain. * @param {string} apiKey The API key. * @param {string} appName The App name. * @param {string} authType The Auth operation type. * @param {!fireauth.AuthProvider} provider The Auth provider that the OAuth * handler request is built to sign in to. * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator * configuration. * @constructor */ fireauth.iframeclient.OAuthUrlBuilder = function(authDomain, apiKey, appName, authType, provider, emulatorConfig) { /** @private {string} The application authDomain. */ this.authDomain_ = authDomain; /** @private {string} The API key. */ this.apiKey_ = apiKey; /** @private {string} The App name. */ this.appName_ = appName; /** @private {string} The Auth operation type. */ this.authType_ = authType; /** * @private @const {?fireauth.constants.EmulatorSettings|undefined} * The emulator configuration. */ this.emulatorConfig_ = emulatorConfig; /** * @private {?string|undefined} The redirect URL used in redirect operations. */ this.redirectUrl_ = null; /** @private {?string|undefined} The event ID. */ this.eventId_ = null; /** @private {?string|undefined} The client version. */ this.v_ = null; /** * @private {!fireauth.AuthProvider} The Firebase Auth provider that the OAuth * handler request is built to sign in to. */ this.provider_ = provider; /** @private {?string|undefined} The endpoint ID. */ this.endpointId_ = null; /** @private {?string|undefined} The tenant ID. */ this.tenantId_ = null; }; /** * Sets the redirect URL. * @param {?string|undefined} redirectUrl The redirect URL used in redirect * operations. * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL * builder instance. */ fireauth.iframeclient.OAuthUrlBuilder.prototype.setRedirectUrl = function(redirectUrl) { this.redirectUrl_ = redirectUrl; return this; }; /** * Sets the event ID. * @param {?string|undefined} eventId The event ID. * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL * builder instance. */ fireauth.iframeclient.OAuthUrlBuilder.prototype.setEventId = function(eventId) { this.eventId_ = eventId; return this; }; /** * Sets the tenant ID. * @param {?string|undefined} tenantId The event ID. * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL * builder instance. */ fireauth.iframeclient.OAuthUrlBuilder.prototype.setTenantId = function(tenantId) { this.tenantId_ = tenantId; return this; }; /** * Sets the client version. * @param {?string|undefined} v The client version. * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL * builder instance. */ fireauth.iframeclient.OAuthUrlBuilder.prototype.setVersion = function(v) { this.v_ = v; return this; }; /** * Sets the endpoint ID. * @param {?string|undefined} eid The endpoint ID (staging, test Gaia, etc). * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL * builder instance. */ fireauth.iframeclient.OAuthUrlBuilder.prototype.setEndpointId = function(eid) { this.endpointId_ = eid; return this; }; /** * Sets any additional optional parameters. This will overwrite any previously * set additional parameters. * @param {?Object<string, string>|undefined} additionalParams The optional * additional parameters. * @return {!fireauth.iframeclient.OAuthUrlBuilder} The current OAuth URL * builder instance. */ fireauth.iframeclient.OAuthUrlBuilder.prototype.setAdditionalParameters = function(additionalParams) { this.additionalParams_ = goog.object.clone(additionalParams || null); return this; }; /** * Modifies the URI with the relevant Auth provider parameters. * @return {string} The constructed OAuth URL string. * @override */ fireauth.iframeclient.OAuthUrlBuilder.prototype.toString = function () { var uri; if (this.emulatorConfig_) { const emulatorUri = goog.Uri.parse(this.emulatorConfig_.url); uri = goog.Uri.create( emulatorUri.getScheme(), null, emulatorUri.getDomain(), emulatorUri.getPort(), '/emulator/auth/handler', null, null); } else { uri = goog.Uri.create( fireauth.iframeclient.SCHEME, null, this.authDomain_, fireauth.iframeclient.PORT_NUMBER, '/__/auth/handler', null, null); } uri.setParameterValue('apiKey', this.apiKey_); uri.setParameterValue('appName', this.appName_); uri.setParameterValue('authType', this.authType_); // Add custom parameters for OAuth1/OAuth2 providers. if (this.provider_['isOAuthProvider']) { // Set default language if available and no language already set. /** @type {!fireauth.FederatedProvider} */ (this.provider_) .setDefaultLanguage(this.getAuthLanguage_()); uri.setParameterValue('providerId', this.provider_['providerId']); var customParameters = /** @type {!fireauth.FederatedProvider} */ ( this.provider_).getCustomParameters(); if (!goog.object.isEmpty(customParameters)) { uri.setParameterValue( 'customParameters', /** @type {string} */ (fireauth.util.stringifyJSON(customParameters)) ); } } // Add scopes for OAuth2 providers. if (typeof this.provider_.getScopes === 'function') { var scopes = this.provider_.getScopes(); if (scopes.length) { uri.setParameterValue('scopes', scopes.join(',')); } } if (this.redirectUrl_) { uri.setParameterValue('redirectUrl', this.redirectUrl_); } else { uri.removeParameter('redirectUrl'); } if (this.eventId_) { uri.setParameterValue('eventId', this.eventId_); } else { uri.removeParameter('eventId'); } // Pass the client version if available. if (this.v_) { uri.setParameterValue('v', this.v_); } else { uri.removeParameter('v'); } if (this.additionalParams_) { for (var key in this.additionalParams_) { if (this.additionalParams_.hasOwnProperty(key) && // Don't overwrite other existing parameters. !uri.getParameterValue(key)) { uri.setParameterValue(key, this.additionalParams_[key]); } } } // Pass the tenant ID if available. if (this.tenantId_) { uri.setParameterValue('tid', this.tenantId_); } else { uri.removeParameter('tid'); } // Pass the endpoint ID if available. if (this.endpointId_) { uri.setParameterValue('eid', this.endpointId_); } else { uri.removeParameter('eid'); } // Append any framework IDs to the handler URL to log in handler RPC requests. var frameworks = this.getAuthFrameworks_(); if (frameworks.length) { uri.setParameterValue('fw', frameworks.join(',')); } return uri.toString(); }; /** * Returns the current Auth instance's language code. * @return {?string} The corresponding language code. * @private */ fireauth.iframeclient.OAuthUrlBuilder.prototype.getAuthLanguage_ = function() { try { // Get the Auth instance for the current App identified by the App name. // This could fail if, for example, the App instance was deleted. return firebase['app'](this.appName_)['auth']().getLanguageCode(); } catch (e) { return null; } }; /** * Returns the list of Firebase frameworks used for logging purposes. * @return {!Array<string>} The list of corresponding Firebase frameworks. * @private */ fireauth.iframeclient.OAuthUrlBuilder.prototype.getAuthFrameworks_ = function() { return fireauth.iframeclient.OAuthUrlBuilder.getAuthFrameworksForApp_( this.appName_); }; /** * Returns the list of Firebase frameworks used for logging purposes * corresponding to the Firebase App name provided. * @param {string} appName The Firebase App name. * @return {!Array<string>} The list of corresponding Firebase frameworks. * @private */ fireauth.iframeclient.OAuthUrlBuilder.getAuthFrameworksForApp_ = function(appName) { try { // Get the Auth instance's list of Firebase framework IDs for the current // App identified by the App name. // This could fail if, for example, the App instance was deleted. return firebase['app'](appName)['auth']().getFramework(); } catch (e) { return []; } }; /** * Initializes the ifcHandler which provides the mechanism to listen to Auth * events on the hidden iframe. * @param {string} authDomain The firebase authDomain used to determine the * OAuth helper page domain. * @param {string} apiKey The API key for sending backend Auth requests. * @param {string} appName The App ID for the Auth instance that triggered this * request. * @param {?string=} opt_clientVersion The optional client version string. * @param {?string=} opt_endpointId The endpoint ID (staging, test Gaia, etc). * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator * configuration. * @constructor * @implements {fireauth.OAuthSignInHandler} */ fireauth.iframeclient.IfcHandler = function(authDomain, apiKey, appName, opt_clientVersion, opt_endpointId, emulatorConfig) { /** @private {string} The Auth domain. */ this.authDomain_ = authDomain; /** @private {string} The API key. */ this.apiKey_ = apiKey; /** @private {string} The App name. */ this.appName_ = appName; /** * @private @const {?fireauth.constants.EmulatorSettings|undefined} * The emulator configuration. */ this.emulatorConfig_ = emulatorConfig; /** @private {?string} The client version. */ this.clientVersion_ = opt_clientVersion || null; /** @private {?string} The Auth endpoint ID. */ this.endpointId_ = opt_endpointId || null; // Delay RPC handler and iframe URL initialization until needed to ensure // logged frameworks are propagated to the iframe. /** @private {?string} The full client version string. */ this.fullClientVersion_ = null; /** @private {?string} The iframe URL. */ this.iframeUrl_ = null; /** @private {?fireauth.RpcHandler} The RPC handler for provided API key. */ this.rpcHandler_ = null; /** * @private {!Array<!function(?fireauth.AuthEvent)>} The Auth event * listeners. */ this.authEventListeners_ = []; // Delay origin validator determination until needed, so the error is not // thrown in the background. This will also prevent the getProjectConfig RPC // until it is required. /** @private {?goog.Promise} The origin validator. */ this.originValidator_ = null; /** @private {?goog.Promise} The initialization promise. */ this.isInitialized_ = null; }; /** * Validates the provided URL. * @param {!fireauth.RpcHandler} rpcHandler The RPC handler used to validate the * requested origin. * @param {string=} opt_origin The optional page origin. If not provided, the * window.location.href value is used. * @return {!goog.Promise} The promise that resolves if the provided origin is * valid. * @private */ fireauth.iframeclient.IfcHandler.getOriginValidator_ = function(rpcHandler, opt_origin) { var origin = opt_origin || fireauth.util.getCurrentUrl(); return rpcHandler.getAuthorizedDomains().then(function(authorizedDomains) { if (!fireauth.util.isAuthorizedDomain(authorizedDomains, origin)) { throw new fireauth.InvalidOriginError(fireauth.util.getCurrentUrl()); } }); }; /** * Initializes the iframe client wrapper. * @return {!goog.Promise} The promise that resolves on initialization. */ fireauth.iframeclient.IfcHandler.prototype.initialize = function() { // Already initialized. if (this.isInitialized_) { return this.isInitialized_; } var self = this; this.isInitialized_ = fireauth.util.onDomReady().then(function() { /** * @private {!fireauth.iframeclient.IframeWrapper} The iframe wrapper * instance. */ self.iframeWrapper_ = new fireauth.iframeclient.IframeWrapper( self.getIframeUrl()); // Register all event listeners to Auth event messages sent from Auth // iframe. self.registerEvents_(); }); return this.isInitialized_; }; /** * Waits for popup window to close. When closed start timeout listener for popup * pending promise. If in the process, it was detected that the iframe does not * support web storage, the popup is closed and the web storage unsupported * error is thrown. * @param {!Window} popupWin The popup window. * @param {!function(!fireauth.AuthError)} onError The on error callback. * @param {number} timeoutDuration The time to wait in ms after the popup is * closed before triggering the popup closed by user error. * @return {!goog.Promise} * @override */ fireauth.iframeclient.IfcHandler.prototype.startPopupTimeout = function(popupWin, onError, timeoutDuration) { // Expire pending timeout promise for popup operation. var popupClosedByUserError = new fireauth.AuthError( fireauth.authenum.Error.POPUP_CLOSED_BY_USER); // If web storage is disabled in the iframe, expire popup timeout quickly with // this error. var webStorageNotSupportedError = new fireauth.AuthError( fireauth.authenum.Error.WEB_STORAGE_UNSUPPORTED); var self = this; var isResolved = false; // Wait for the iframe to be ready first. return this.initializeAndWait().then(function() { // We do not return isWebStorageSupported() to ensure that this is backward // compatible. // Pushing the following client changes before updating the iframe to // respond to these events would continue to work. // The downside is that the popup could be closed before this resolves. // In that case, they would get an error that the popup was closed and not // the error that web storage is not supported, though that is unlikely // as isWebStorageSupported should execute faster than the popup timeout. // If web storage is not supported in the iframe, fail quickly. self.isWebStorageSupported().then(function(isSupported) { if (!isSupported) { // If not supported, close window. if (popupWin) { fireauth.util.closeWindow(popupWin); } onError(webStorageNotSupportedError); isResolved = true; } }); }).thenCatch(function(error) { // Ignore any possible error in iframe embedding. // These types of errors will be handled in processPopup which will close // the popup too if that happens. return; }).then(function() { // Skip if already resolved. if (isResolved) { return; } // After the iframe is ready, wait for popup to close and then start timeout // check. return fireauth.util.onPopupClose(popupWin); }).then(function() { // Skip if already resolved. if (isResolved) { return; } return goog.Timer.promise(timeoutDuration).then(function() { // If this is already resolved or rejected, this will do nothing. onError(popupClosedByUserError); }); }); }; /** * @return {boolean} Whether the handler should be initialized early. * @override */ fireauth.iframeclient.IfcHandler.prototype.shouldBeInitializedEarly = function() { var ua = fireauth.util.getUserAgentString(); // Cannot run in the background (can't wait for iframe to be embedded // before triggering popup redirect) and is Safari (can only detect // localStorage in iframe via change event) => embed iframe ASAP. // Do the same for mobile browsers on iOS devices as they use the same // Safari implementation underneath. return !fireauth.util.runsInBackground(ua) && !fireauth.util.iframeCanSyncWebStorage(ua); }; /** * @return {boolean} Whether the sign-in handler in the current environment * has volatile session storage. * @override */ fireauth.iframeclient.IfcHandler.prototype.hasVolatileStorage = function() { // Web environment with web storage enabled has stable sessionStorage. return false; }; /** * Processes the popup request. The popup instance must be provided externally * and on error, the requestor must close the window. * @param {?Window} popupWin The popup window reference. * @param {!fireauth.AuthEvent.Type} mode The Auth event type. * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with. * @param {function()} onInitialize The function to call on initialization. * @param {function(*)} onError The function to call on error. * @param {string=} opt_eventId The optional event ID. * @param {boolean=} opt_alreadyRedirected Whether popup is already redirected * to final destination. * @param {?string=} opt_tenantId The optional tenant ID. * @return {!goog.Promise} The popup window promise. * @override */ fireauth.iframeclient.IfcHandler.prototype.processPopup = function( popupWin, mode, provider, onInitialize, onError, opt_eventId, opt_alreadyRedirected, opt_tenantId) { // processPopup is failing since it tries to access popup win when tab can // not run in background. For now bypass processPopup which runs // additional origin check not accounted above. Besides, iframe will never // hand result to parent if origin not whitelisted. // Error thrown by browser: Unable to establish a connection with the // popup. It may have been blocked by the browser. // If popup is null, startPopupTimeout will catch it without having the // above error getting triggered due to popup access from opener. // Reject immediately if the popup is blocked. if (!popupWin) { return goog.Promise.reject( new fireauth.AuthError(fireauth.authenum.Error.POPUP_BLOCKED)); } // Already redirected and cannot run in the background, resolve quickly while // initializing. if (opt_alreadyRedirected && !fireauth.util.runsInBackground()) { // Initialize first before resolving. this.initializeAndWait().thenCatch(function(error) { fireauth.util.closeWindow(popupWin); onError(error); }); onInitialize(); // Already redirected. return goog.Promise.resolve(); } // If origin validator not determined yet. if (!this.originValidator_) { this.originValidator_ = fireauth.iframeclient.IfcHandler.getOriginValidator_( this.getRpcHandler_()); } var self = this; return this.originValidator_.then(function() { // After origin validation, wait for iframe to be ready before redirecting. var onReady = self.initializeAndWait().thenCatch(function(error) { fireauth.util.closeWindow(popupWin); onError(error); throw error; }); onInitialize(); return onReady; }).then(function() { // Popup and redirect operations work for OAuth providers only. fireauth.AuthProvider.checkIfOAuthSupported(provider); // Already redirected to intended destination, no need to redirect again. if (opt_alreadyRedirected) { return; } var oauthHelperWidgetUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( self.authDomain_, self.apiKey_, self.appName_, mode, provider, null, opt_eventId, self.clientVersion_, undefined, self.endpointId_, opt_tenantId, self.emulatorConfig_); // Redirect popup to OAuth helper widget URL. fireauth.util.goTo(oauthHelperWidgetUrl, /** @type {!Window} */ (popupWin)); }).thenCatch(function(e) { // Force another origin validation. if (e.code == 'auth/network-request-failed') { self.originValidator_ = null; } throw e; }); }; /** * @return {!fireauth.RpcHandler} The RPC handler instance with the relevant * endpoints, version and frameworks. * @private */ fireauth.iframeclient.IfcHandler.prototype.getRpcHandler_ = function() { if (!this.rpcHandler_) { this.fullClientVersion_ = this.clientVersion_ ? fireauth.util.getClientVersion( fireauth.util.ClientImplementation.JSCORE, this.clientVersion_, fireauth.iframeclient.OAuthUrlBuilder.getAuthFrameworksForApp_( this.appName_)) : null; this.rpcHandler_ = new fireauth.RpcHandler( this.apiKey_, // Get the client Auth endpoint used. fireauth.constants.getEndpointConfig(this.endpointId_), this.fullClientVersion_); if (this.emulatorConfig_) { this.rpcHandler_.updateEmulatorConfig(this.emulatorConfig_); } } return this.rpcHandler_; }; /** * Processes the redirect request. * @param {!fireauth.AuthEvent.Type} mode The Auth event type. * @param {!fireauth.AuthProvider} provider The Auth provider to sign in with. * @param {?string=} opt_eventId The optional event ID. * @param {?string=} opt_tenantId The optional tenant ID. * @return {!goog.Promise} * @override */ fireauth.iframeclient.IfcHandler.prototype.processRedirect = function(mode, provider, opt_eventId, opt_tenantId) { // If origin validator not determined yet. if (!this.originValidator_) { this.originValidator_ = fireauth.iframeclient.IfcHandler.getOriginValidator_( this.getRpcHandler_()); } var self = this; // Make sure origin is validated. return this.originValidator_.then(function() { fireauth.AuthProvider.checkIfOAuthSupported(provider); var oauthHelperWidgetUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( self.authDomain_, self.apiKey_, self.appName_, mode, provider, fireauth.util.getCurrentUrl(), opt_eventId, self.clientVersion_, undefined, self.endpointId_, opt_tenantId, self.emulatorConfig_); // Redirect to OAuth helper widget URL. fireauth.util.goTo(oauthHelperWidgetUrl); }).thenCatch(function(e) { // Force another origin validation on network errors. if (e.code == 'auth/network-request-failed') { self.originValidator_ = null; } throw e; }); }; /** @return {string} The iframe URL. */ fireauth.iframeclient.IfcHandler.prototype.getIframeUrl = function() { if (!this.iframeUrl_) { this.iframeUrl_ = fireauth.iframeclient.IfcHandler.getAuthIframeUrl( this.authDomain_, this.apiKey_, this.appName_, this.clientVersion_, this.endpointId_, fireauth.iframeclient.OAuthUrlBuilder.getAuthFrameworksForApp_( this.appName_), this.emulatorConfig_); } return this.iframeUrl_; }; /** * @return {!goog.Promise} The promise that resolves when the iframe is ready. * @override */ fireauth.iframeclient.IfcHandler.prototype.initializeAndWait = function() { // Initialize if not initialized yet. var self = this; return this.initialize().then(function() { return self.iframeWrapper_.onReady(); }).thenCatch(function(error) { // Reset origin validator. self.originValidator_ = null; // Reject iframe ready promise with network error. throw new fireauth.AuthError( fireauth.authenum.Error.NETWORK_REQUEST_FAILED); }); }; /** * @return {boolean} Whether the handler will unload the current page on * redirect operations. * @override */ fireauth.iframeclient.IfcHandler.prototype.unloadsOnRedirect = function() { return true; }; /** * @param {string} authDomain The Firebase authDomain used to determine the * OAuth helper page domain. * @param {string} apiKey The API key for sending backend Auth requests. * @param {string} appName The App ID for the Auth instance that triggered this * request. * @param {?string=} opt_clientVersion The optional client version string. * @param {?string=} opt_endpointId The endpoint ID (staging, test Gaia, etc). * @param {?Array<string>=} opt_frameworks The optional list of framework IDs. * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator * configuration. * @return {string} The data iframe src URL. */ fireauth.iframeclient.IfcHandler.getAuthIframeUrl = function(authDomain, apiKey, appName, opt_clientVersion, opt_endpointId, opt_frameworks, emulatorConfig) { // OAuth helper iframe URL. var builder = new fireauth.iframeclient.IframeUrlBuilder( authDomain, apiKey, appName, emulatorConfig); return builder .setVersion(opt_clientVersion) .setEndpointId(opt_endpointId) .setFrameworks(opt_frameworks) .toString(); }; /** * @param {string} authDomain The Firebase authDomain used to determine the * OAuth helper page domain. * @param {string} apiKey The API key for sending backend Auth requests. * @param {string} appName The App ID for the Auth instance that triggered this * request. * @param {string} authType The type of operation that depends on OAuth sign in. * @param {!fireauth.AuthProvider} provider The provider to sign in to. * @param {?string=} opt_redirectUrl The optional URL to redirect to on OAuth * sign in completion. * @param {?string=} opt_eventId The optional event ID to identify on receipt. * @param {?string=} opt_clientVersion The optional client version string. * @param {?Object<string, string>=} opt_additionalParams The optional * additional parameters. * @param {?string=} opt_endpointId The endpoint ID (staging, test Gaia, etc). * @param {?string=} opt_tenantId The optional tenant ID. * @param {?fireauth.constants.EmulatorSettings=} emulatorConfig The emulator * configuration. * @return {string} The OAuth helper widget URL. */ fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl = function( authDomain, apiKey, appName, authType, provider, opt_redirectUrl, opt_eventId, opt_clientVersion, opt_additionalParams, opt_endpointId, opt_tenantId, emulatorConfig) { // OAuth helper widget URL. var builder = new fireauth.iframeclient.OAuthUrlBuilder( authDomain, apiKey, appName, authType, provider, emulatorConfig); return builder .setRedirectUrl(opt_redirectUrl) .setEventId(opt_eventId) .setVersion(opt_clientVersion) .setAdditionalParameters(opt_additionalParams) .setEndpointId(opt_endpointId) .setTenantId(opt_tenantId) .toString(); }; /** * Post message receiver event names. * @enum {string} */ fireauth.iframeclient.IfcHandler.ReceiverEvent = { AUTH_EVENT: 'authEvent' }; /** * Post message sender event names. * @enum {string} */ fireauth.iframeclient.IfcHandler.SenderEvent = { WEB_STORAGE_SUPPORT_EVENT: 'webStorageSupport' }; /** * Post message response field names. * @enum {string} */ fireauth.iframeclient.IfcHandler.Response = { STATUS: 'status', AUTH_EVENT: 'authEvent', WEB_STORAGE_SUPPORT: 'webStorageSupport' }; /** * Post message status values. * @enum {string} */ fireauth.iframeclient.IfcHandler.Status = { ACK: 'ACK', ERROR: 'ERROR' }; /** * Registers all event listeners. * @private */ fireauth.iframeclient.IfcHandler.prototype.registerEvents_ = function() { // Should be run in initialization. if (!this.iframeWrapper_) { throw new Error('IfcHandler must be initialized!'); } var self = this; // Listen to Auth change events emitted from iframe. this.iframeWrapper_.registerEvent( fireauth.iframeclient.IfcHandler.ReceiverEvent.AUTH_EVENT, function(response) { var resolveResponse = {}; if (response && response[fireauth.iframeclient.IfcHandler.Response.AUTH_EVENT]) { var isHandled = false; // Get Auth event (plain object). var authEvent = fireauth.AuthEvent.fromPlainObject( response[fireauth.iframeclient.IfcHandler.Response.AUTH_EVENT]); // Trigger Auth change on all listeners. for (var i = 0; i < self.authEventListeners_.length; i++) { isHandled = self.authEventListeners_[i](authEvent) || isHandled; } // Return ack response to notify sender of success. resolveResponse = {}; resolveResponse[fireauth.iframeclient.IfcHandler.Response.STATUS] = isHandled ? fireauth.iframeclient.IfcHandler.Status.ACK : fireauth.iframeclient.IfcHandler.Status.ERROR; return goog.Promise.resolve(resolveResponse); } // Return error status if the response is invalid. resolveResponse[fireauth.iframeclient.IfcHandler.Response.STATUS] = fireauth.iframeclient.IfcHandler.Status.ERROR; return goog.Promise.resolve(resolveResponse); }); }; /** * @return {!goog.Promise<boolean>} Whether web storage is supported in the * iframe. */ fireauth.iframeclient.IfcHandler.prototype.isWebStorageSupported = function() { var webStorageSupportEvent = fireauth.iframeclient.IfcHandler.SenderEvent.WEB_STORAGE_SUPPORT_EVENT; var message = { 'type': webStorageSupportEvent }; var self = this; // Initialize if not initialized yet. return this.initialize().then(function() { return self.iframeWrapper_.sendMessage(message); }).then(function(response) { // Parse the response and return the passed web storage support status. var key = fireauth.iframeclient.IfcHandler.Response.WEB_STORAGE_SUPPORT; if (response && response.length && typeof response[0][key] !== 'undefined') { return response[0][key]; } // Internal error. throw new Error; }); }; /** * @param {!function(?fireauth.AuthEvent):boolean} listener The Auth event * listener to add. * @override */ fireauth.iframeclient.IfcHandler.prototype.addAuthEventListener = function(listener) { this.authEventListeners_.push(listener); }; /** * @param {!function(?fireauth.AuthEvent):boolean} listener The Auth event * listener to remove. * @override */ fireauth.iframeclient.IfcHandler.prototype.removeAuthEventListener = function(listener) { goog.array.removeAllIf(this.authEventListeners_, function(ele) { return ele == listener; }); };