UNPKG

voluptasmollitia

Version:
1,512 lines (1,363 loc) 109 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 Utility for handling RPC requests to server. */ goog.provide('fireauth.RpcHandler'); goog.provide('fireauth.RpcHandler.ApiMethodHandler'); goog.provide('fireauth.RpcHandler.VerifyAssertionData'); goog.provide('fireauth.XmlHttpFactory'); goog.require('fireauth.AuthError'); goog.require('fireauth.AuthErrorWithCredential'); goog.require('fireauth.authenum.Error'); goog.require('fireauth.constants'); goog.require('fireauth.idp'); goog.require('fireauth.idp.ProviderId'); goog.require('fireauth.object'); goog.require('fireauth.util'); goog.require('goog.Promise'); goog.require('goog.Uri'); goog.require('goog.html.TrustedResourceUrl'); goog.require('goog.json'); goog.require('goog.net.CorsXmlHttpFactory'); goog.require('goog.net.EventType'); goog.require('goog.net.FetchXmlHttpFactory'); goog.require('goog.net.XhrIo'); goog.require('goog.net.XmlHttpFactory'); goog.require('goog.net.jsloader'); goog.require('goog.object'); goog.require('goog.string.Const'); /** * Firebase Auth XmlHttpRequest factory. This is useful for environments like * Node.js where XMLHttpRequest does not exist. XmlHttpFactory would be * initialized using the polyfill XMLHttpRequest module. * @param {function(new:XMLHttpRequest)} xmlHttpRequest The xmlHttpRequest * constructor. * @constructor * @extends {goog.net.XmlHttpFactory} * @final */ fireauth.XmlHttpFactory = function(xmlHttpRequest) { /** * @private {function(new:XMLHttpRequest)} The underlying XHR reference. */ this.xmlHttpRequest_ = xmlHttpRequest; fireauth.XmlHttpFactory.base(this, 'constructor'); }; goog.inherits(fireauth.XmlHttpFactory, goog.net.XmlHttpFactory); /** * @return {!goog.net.XhrLike|!XMLHttpRequest} A new XhrLike instance. * @override */ fireauth.XmlHttpFactory.prototype.createInstance = function() { return new this.xmlHttpRequest_(); }; /** * @return {!Object} Options describing how XHR objects obtained from this * factory should be used. * @override */ fireauth.XmlHttpFactory.prototype.internalGetOptions = function() { return {}; }; /** * Creates an RPC request handler for the project specified by the API key. * * @param {string} apiKey The API key. * @param {?Object=} opt_config The RPC request processor configuration. * @param {?string=} opt_firebaseClientVersion The optional Firebase client * version to log with requests to Firebase Auth server. * @constructor */ fireauth.RpcHandler = function(apiKey, opt_config, opt_firebaseClientVersion) { /** @private {string} The project API key. */ this.apiKey_ = apiKey; var config = opt_config || {}; this.secureTokenEndpoint_ = config['secureTokenEndpoint'] || fireauth.RpcHandler.SECURE_TOKEN_ENDPOINT_; /** * @private @const {!fireauth.util.Delay} The delay for secure token endpoint * network timeout. */ this.secureTokenTimeout_ = config['secureTokenTimeout'] || fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_TIMEOUT_; /** @private @const {!Object} The secure token server headers. */ this.secureTokenHeaders_ = goog.object.clone( config['secureTokenHeaders'] || fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_HEADERS_); /** @private {string} The Firebase Auth endpoint. */ this.firebaseEndpoint_ = config['firebaseEndpoint'] || fireauth.RpcHandler.FIREBASE_ENDPOINT_; /** @private {string} The identity platform endpoint. */ this.identityPlatformEndpoint_ = config['identityPlatformEndpoint'] || fireauth.RpcHandler.IDENTITY_PLATFORM_ENDPOINT_; /** * @private @const {!fireauth.util.Delay} The delay for Firebase Auth endpoint * network timeout. */ this.firebaseTimeout_ = config['firebaseTimeout'] || fireauth.RpcHandler.DEFAULT_FIREBASE_TIMEOUT_; this.firebaseHeaders_ = goog.object.clone( config['firebaseHeaders'] || fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_); // If Firebase client version needs to be logged too. if (opt_firebaseClientVersion) { // Log client version for Firebase Auth server. this.firebaseHeaders_['X-Client-Version'] = opt_firebaseClientVersion; // Log client version for securetoken server. this.secureTokenHeaders_['X-Client-Version'] = opt_firebaseClientVersion; } // Get XMLHttpRequest reference. var XMLHttpRequest = fireauth.RpcHandler.getXMLHttpRequest(); if (!XMLHttpRequest && !fireauth.util.isWorker()) { // In a Node.js environment, xmlhttprequest module needs to be required. throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR, 'The XMLHttpRequest compatibility library was not found.'); } /** @private {!goog.net.XmlHttpFactory|undefined} The XHR factory. */ this.rpcHandlerXhrFactory_ = undefined; // Initialize XHR factory. CORS does not apply in native environments or // workers so don't use CorsXmlHttpFactory in those cases. if (fireauth.util.isWorker()) { // For worker environment use FetchXmlHttpFactory. this.rpcHandlerXhrFactory_ = new goog.net.FetchXmlHttpFactory( /** @type {!WorkerGlobalScope} */ (self)); } else if (fireauth.util.isNativeEnvironment()) { // For Node.js, this is the polyfill library. For other environments, // this is the native global XMLHttpRequest. this.rpcHandlerXhrFactory_ = new fireauth.XmlHttpFactory( /** @type {function(new:XMLHttpRequest)} */ (XMLHttpRequest)); } else { // CORS Browser environment. this.rpcHandlerXhrFactory_ = new goog.net.CorsXmlHttpFactory(); } /** @private {?string} The tenant ID. */ this.tenantId_ = null; }; /** * @return {?function(new:XMLHttpRequest)|undefined} The current environment * XMLHttpRequest. This is undefined for worker environment. */ fireauth.RpcHandler.getXMLHttpRequest = function() { // In Node.js XMLHttpRequest is polyfilled. var isNode = fireauth.util.getEnvironment() == fireauth.util.Env.NODE; var XMLHttpRequest = goog.global['XMLHttpRequest'] || (isNode && firebase.INTERNAL['node'] && firebase.INTERNAL['node']['XMLHttpRequest']); return XMLHttpRequest; }; /** * Enums for HTTP request methods. * @enum {string} */ fireauth.RpcHandler.HttpMethod = { POST: 'POST', GET: 'GET' }; /** * Firebase Auth server error codes. * @enum {string} */ fireauth.RpcHandler.ServerError = { ADMIN_ONLY_OPERATION: 'ADMIN_ONLY_OPERATION', CAPTCHA_CHECK_FAILED: 'CAPTCHA_CHECK_FAILED', CORS_UNSUPPORTED: 'CORS_UNSUPPORTED', CREDENTIAL_MISMATCH: 'CREDENTIAL_MISMATCH', CREDENTIAL_TOO_OLD_LOGIN_AGAIN: 'CREDENTIAL_TOO_OLD_LOGIN_AGAIN', DYNAMIC_LINK_NOT_ACTIVATED: 'DYNAMIC_LINK_NOT_ACTIVATED', EMAIL_CHANGE_NEEDS_VERIFICATION: 'EMAIL_CHANGE_NEEDS_VERIFICATION', EMAIL_EXISTS: 'EMAIL_EXISTS', EMAIL_NOT_FOUND: 'EMAIL_NOT_FOUND', EXPIRED_OOB_CODE: 'EXPIRED_OOB_CODE', FEDERATED_USER_ID_ALREADY_LINKED: 'FEDERATED_USER_ID_ALREADY_LINKED', INVALID_APP_CREDENTIAL: 'INVALID_APP_CREDENTIAL', INVALID_APP_ID: 'INVALID_APP_ID', INVALID_CERT_HASH: 'INVALID_CERT_HASH', INVALID_CODE: 'INVALID_CODE', INVALID_CONTINUE_URI: 'INVALID_CONTINUE_URI', INVALID_CUSTOM_TOKEN: 'INVALID_CUSTOM_TOKEN', INVALID_DYNAMIC_LINK_DOMAIN: 'INVALID_DYNAMIC_LINK_DOMAIN', INVALID_EMAIL: 'INVALID_EMAIL', INVALID_ID_TOKEN: 'INVALID_ID_TOKEN', INVALID_IDP_RESPONSE: 'INVALID_IDP_RESPONSE', INVALID_IDENTIFIER: 'INVALID_IDENTIFIER', INVALID_MESSAGE_PAYLOAD: 'INVALID_MESSAGE_PAYLOAD', INVALID_MFA_PENDING_CREDENTIAL: 'INVALID_MFA_PENDING_CREDENTIAL', INVALID_OAUTH_CLIENT_ID: 'INVALID_OAUTH_CLIENT_ID', INVALID_OOB_CODE: 'INVALID_OOB_CODE', INVALID_PASSWORD: 'INVALID_PASSWORD', INVALID_PENDING_TOKEN: 'INVALID_PENDING_TOKEN', INVALID_PHONE_NUMBER: 'INVALID_PHONE_NUMBER', INVALID_PROVIDER_ID: 'INVALID_PROVIDER_ID', INVALID_RECIPIENT_EMAIL: 'INVALID_RECIPIENT_EMAIL', INVALID_SENDER: 'INVALID_SENDER', INVALID_SESSION_INFO: 'INVALID_SESSION_INFO', INVALID_TEMPORARY_PROOF: 'INVALID_TEMPORARY_PROOF', INVALID_TENANT_ID: 'INVALID_TENANT_ID', MFA_ENROLLMENT_NOT_FOUND: 'MFA_ENROLLMENT_NOT_FOUND', MISSING_ANDROID_PACKAGE_NAME: 'MISSING_ANDROID_PACKAGE_NAME', MISSING_APP_CREDENTIAL: 'MISSING_APP_CREDENTIAL', MISSING_CODE: 'MISSING_CODE', MISSING_CONTINUE_URI: 'MISSING_CONTINUE_URI', MISSING_CUSTOM_TOKEN: 'MISSING_CUSTOM_TOKEN', MISSING_IOS_BUNDLE_ID: 'MISSING_IOS_BUNDLE_ID', MISSING_MFA_ENROLLMENT_ID: 'MISSING_MFA_ENROLLMENT_ID', MISSING_MFA_PENDING_CREDENTIAL: 'MISSING_MFA_PENDING_CREDENTIAL', MISSING_OOB_CODE: 'MISSING_OOB_CODE', MISSING_OR_INVALID_NONCE: 'MISSING_OR_INVALID_NONCE', MISSING_PASSWORD: 'MISSING_PASSWORD', MISSING_PHONE_NUMBER: 'MISSING_PHONE_NUMBER', MISSING_SESSION_INFO: 'MISSING_SESSION_INFO', OPERATION_NOT_ALLOWED: 'OPERATION_NOT_ALLOWED', PASSWORD_LOGIN_DISABLED: 'PASSWORD_LOGIN_DISABLED', QUOTA_EXCEEDED: 'QUOTA_EXCEEDED', RESET_PASSWORD_EXCEED_LIMIT: 'RESET_PASSWORD_EXCEED_LIMIT', REJECTED_CREDENTIAL: 'REJECTED_CREDENTIAL', SECOND_FACTOR_EXISTS: 'SECOND_FACTOR_EXISTS', SECOND_FACTOR_LIMIT_EXCEEDED: 'SECOND_FACTOR_LIMIT_EXCEEDED', SESSION_EXPIRED: 'SESSION_EXPIRED', TENANT_ID_MISMATCH: 'TENANT_ID_MISMATCH', TOKEN_EXPIRED: 'TOKEN_EXPIRED', TOO_MANY_ATTEMPTS_TRY_LATER: 'TOO_MANY_ATTEMPTS_TRY_LATER', UNSUPPORTED_FIRST_FACTOR: 'UNSUPPORTED_FIRST_FACTOR', UNSUPPORTED_TENANT_OPERATION: 'UNSUPPORTED_TENANT_OPERATION', UNVERIFIED_EMAIL: 'UNVERIFIED_EMAIL', UNAUTHORIZED_DOMAIN: 'UNAUTHORIZED_DOMAIN', USER_CANCELLED: 'USER_CANCELLED', USER_DISABLED: 'USER_DISABLED', USER_NOT_FOUND: 'USER_NOT_FOUND', WEAK_PASSWORD: 'WEAK_PASSWORD' }; /** * A map of server error codes to client errors. * @typedef {!Object< * !fireauth.RpcHandler.ServerError, !fireauth.authenum.Error>} */ fireauth.RpcHandler.ServerErrorMap; /** * Firebase Auth response field names. * @enum {string} */ fireauth.RpcHandler.AuthServerField = { ALL_PROVIDERS: 'allProviders', AUTH_URI: 'authUri', AUTHORIZED_DOMAINS: 'authorizedDomains', DYNAMIC_LINKS_DOMAIN: 'dynamicLinksDomain', EMAIL: 'email', ERROR_MESSAGE: 'errorMessage', EXPIRES_IN: 'expiresIn', ID_TOKEN: 'idToken', MFA_PENDING_CREDENTIAL: 'mfaPendingCredential', NEED_CONFIRMATION: 'needConfirmation', OAUTH_ID_TOKEN: 'oauthIdToken', PENDING_TOKEN: 'pendingToken', PHONE_RESPONSE_INFO: 'phoneResponseInfo', PHONE_SESSION_INFO: 'phoneSessionInfo', POST_BODY: 'postBody', PROVIDER_ID: 'providerId', RECAPTCHA_SITE_KEY: 'recaptchaSiteKey', REQUEST_URI: 'requestUri', REFRESH_TOKEN: 'refreshToken', SESSION_ID: 'sessionId', SESSION_INFO: 'sessionInfo', SIGNIN_METHODS: 'signinMethods', TEMPORARY_PROOF: 'temporaryProof' }; /** * Firebase Auth response injected fields. * @enum {string} */ fireauth.RpcHandler.InjectedResponseField = { NONCE: 'nonce' }; /** * Firebase Auth getOobConfirmationCode requestType possible values. * @enum {string} */ fireauth.RpcHandler.GetOobCodeRequestType = { EMAIL_SIGNIN: 'EMAIL_SIGNIN', NEW_EMAIL_ACCEPT: 'NEW_EMAIL_ACCEPT', PASSWORD_RESET: 'PASSWORD_RESET', VERIFY_AND_CHANGE_EMAIL: 'VERIFY_AND_CHANGE_EMAIL', VERIFY_EMAIL: 'VERIFY_EMAIL' }; /** * Firebase Auth response field names. * @enum {string} */ fireauth.RpcHandler.StsServerField = { ACCESS_TOKEN: 'access_token', EXPIRES_IN: 'expires_in', REFRESH_TOKEN: 'refresh_token' }; /** * @return {string} The API key. */ fireauth.RpcHandler.prototype.getApiKey = function() { return this.apiKey_; }; /** * The Firebase custom locale header. * @const {string} * @private */ fireauth.RpcHandler.FIREBASE_LOCALE_KEY_ = 'X-Firebase-Locale'; /** * The secure token endpoint. * @const {string} * @private */ fireauth.RpcHandler.SECURE_TOKEN_ENDPOINT_ = 'https://securetoken.googleapis.com/v1/token'; /** * The default timeout delay (units in milliseconds) for requests sending to * STS token endpoint. * @const {!fireauth.util.Delay} * @private */ fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_TIMEOUT_ = new fireauth.util.Delay(30000, 60000); /** * The STS token RPC content headers. * @const {!Object} * @private */ fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_HEADERS_ = { 'Content-Type': 'application/x-www-form-urlencoded' }; /** * The Firebase endpoint. * @const {string} * @private */ fireauth.RpcHandler.FIREBASE_ENDPOINT_ = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/'; /** * The Identity Platform endpoint. * @const {string} * @private */ fireauth.RpcHandler.IDENTITY_PLATFORM_ENDPOINT_ = 'https://identitytoolkit.googleapis.com/v2/'; /** * The default timeout delay (units in milliseconds) for requests sending to * Firebase endpoint. * @const {!fireauth.util.Delay} * @private */ fireauth.RpcHandler.DEFAULT_FIREBASE_TIMEOUT_ = new fireauth.util.Delay(30000, 60000); /** * The Firebase RPC content headers. * @const {!Object} * @private */ fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_ = { 'Content-Type': 'application/json' }; /** * Updates the custom locale header. * @param {?string} languageCode The new languageCode. */ fireauth.RpcHandler.prototype.updateCustomLocaleHeader = function(languageCode) { if (languageCode) { // If a language code is provided, add it to the header. this.firebaseHeaders_[fireauth.RpcHandler.FIREBASE_LOCALE_KEY_] = languageCode; } else { // Otherwise remove the custom locale header. delete this.firebaseHeaders_[fireauth.RpcHandler.FIREBASE_LOCALE_KEY_]; } }; /** * Updates the emulator configuration. * @param {?fireauth.constants.EmulatorSettings} emulatorConfig The new * emulator config. */ fireauth.RpcHandler.prototype.updateEmulatorConfig = function(emulatorConfig) { if (!emulatorConfig) { return; } // If an emulator config is provided, update the endpoints. this.secureTokenEndpoint_ = fireauth.RpcHandler.generateEmululatorEndpointUrl_( fireauth.RpcHandler.SECURE_TOKEN_ENDPOINT_, emulatorConfig); this.firebaseEndpoint_ = fireauth.RpcHandler.generateEmululatorEndpointUrl_( fireauth.RpcHandler.FIREBASE_ENDPOINT_, emulatorConfig); this.identityPlatformEndpoint_ = fireauth.RpcHandler.generateEmululatorEndpointUrl_( fireauth.RpcHandler.IDENTITY_PLATFORM_ENDPOINT_, emulatorConfig); } /** * Creates an endpoint URL intended for use by the emulator. * @param {string} endpoint the production endpoint URL. * @param {?fireauth.constants.EmulatorSettings} emulatorConfig The emulator * config. * @return {string} The emulator endpoint URL. * @private */ fireauth.RpcHandler.generateEmululatorEndpointUrl_ = function(endpoint, emulatorConfig) { const uri = goog.Uri.parse(endpoint); const emulatorUri = goog.Uri.parse(emulatorConfig.url); uri.setPath(uri.getDomain() + uri.getPath()); uri.setScheme(emulatorUri.getScheme()); uri.setDomain(emulatorUri.getDomain()); uri.setPort(emulatorUri.getPort()); return uri.toString(); } /** * Updates the X-Client-Version in the header. * @param {?string} clientVersion The new client version. */ fireauth.RpcHandler.prototype.updateClientVersion = function(clientVersion) { if (clientVersion) { // Update client version for Firebase Auth server. this.firebaseHeaders_['X-Client-Version'] = clientVersion; // Update client version for securetoken server. this.secureTokenHeaders_['X-Client-Version'] = clientVersion; } else { // Remove client version from header. delete this.firebaseHeaders_['X-Client-Version']; delete this.secureTokenHeaders_['X-Client-Version']; } }; /** * Updates the tenant ID in the request. * @param {?string} tenantId The new tenant ID. */ fireauth.RpcHandler.prototype.updateTenantId = function(tenantId) { this.tenantId_ = tenantId; }; /** * Returns the tenant ID. * @return {?string} The tenant ID. */ fireauth.RpcHandler.prototype.getTenantId = function() { return this.tenantId_; }; /** * Sends XhrIo request using goog.net.XhrIo. * @param {string} url The URL to make a request to. * @param {function(?Object)=} opt_callback The callback to run on completion. * @param {fireauth.RpcHandler.HttpMethod=} opt_httpMethod The HTTP send method. * @param {?ArrayBuffer|?ArrayBufferView|?Blob|?Document|?FormData|string=} * opt_data The request content. * @param {?Object=} opt_headers The request content headers. * @param {number=} opt_timeout The request timeout. * @private */ fireauth.RpcHandler.prototype.sendXhr_ = function( url, opt_callback, opt_httpMethod, opt_data, opt_headers, opt_timeout) { var sendXhr; if (fireauth.util.supportsCors() || fireauth.util.isWorker()) { // If supports CORS use goog.net.XhrIo. sendXhr = goog.bind(this.sendXhrUsingXhrIo_, this); } else { // Load gapi.client.request and gapi.auth dependency dynamically. if (!fireauth.RpcHandler.loadGApi_) { fireauth.RpcHandler.loadGApi_ = new goog.Promise(function(resolve, reject) { // On load, resolve. fireauth.RpcHandler.loadGApiJs_(resolve, reject); }); } // If does not support CORS, use gapi.client.request. sendXhr = goog.bind(this.sendXhrUsingGApiClient_, this); } sendXhr( url, opt_callback, opt_httpMethod, opt_data, opt_headers, opt_timeout); }; /** * Sends XhrIo request using goog.net.XhrIo. * @param {string} url The URL to make a request to. * @param {function(?Object)=} opt_callback The callback to run on completion. * @param {fireauth.RpcHandler.HttpMethod=} opt_httpMethod The HTTP send method. * @param {?ArrayBuffer|?ArrayBufferView|?Blob|?Document|?FormData|string=} * opt_data The request content. * @param {?Object=} opt_headers The request content headers. * @param {number=} opt_timeout The request timeout. * @private */ fireauth.RpcHandler.prototype.sendXhrUsingXhrIo_ = function( url, opt_callback, opt_httpMethod, opt_data, opt_headers, opt_timeout) { if (fireauth.util.isWorker() && !fireauth.util.isFetchSupported()) { throw new fireauth.AuthError( fireauth.authenum.Error.OPERATION_NOT_SUPPORTED, 'fetch, Headers and Request native APIs or equivalent Polyfills ' + 'must be available to support HTTP requests from a Worker ' + 'environment.'); } var xhrIo = new goog.net.XhrIo(this.rpcHandlerXhrFactory_); // xhrIo.setTimeoutInterval not working in IE10 and IE11, handle manually. var requestTimeout; if (opt_timeout) { xhrIo.setTimeoutInterval(opt_timeout); requestTimeout = setTimeout(function() { xhrIo.dispatchEvent(goog.net.EventType.TIMEOUT); }, opt_timeout); } // Run callback function on completion. xhrIo.listen( goog.net.EventType.COMPLETE, /** @this {goog.net.XhrIo} */ function() { // Clear timeout timer. if (requestTimeout) { clearTimeout(requestTimeout); } // Response assumed to be in json format. If not, catch, log error and // pass null to callback. var response = null; try { // Do not use this.responseJson() as it uses goog.json.parse // underneath. Internal goog.json.parse parsing uses eval and since // recommended Content Security Policy does not allow unsafe-eval, // this is failing and throwing an error in chrome extensions and // warnings else where. Use native parsing instead via JSON.parse. response = JSON.parse(this.getResponseText()) || null; } catch (e) { response = null; } if (opt_callback) { opt_callback(/** @type {?Object} */ (response)); } }); // Dispose xhrIo on ready. xhrIo.listenOnce( goog.net.EventType.READY, /** @this {goog.net.XhrIo} */ function() { // Clear timeout timer. if (requestTimeout) { clearTimeout(requestTimeout); } // Dispose xhrIo. this.dispose(); }); // Listen to timeout error. // This should work when request is aborted too. xhrIo.listenOnce( goog.net.EventType.TIMEOUT, /** @this {goog.net.XhrIo} */ function() { // Clear timeout timer. if (requestTimeout) { clearTimeout(requestTimeout); } // Dispose xhrIo. this.dispose(); // The request timed out. if (opt_callback) { opt_callback(null); } }); xhrIo.send(url, opt_httpMethod, opt_data, opt_headers); }; /** * @const {!goog.string.Const} The GApi client library URL. * @private */ fireauth.RpcHandler.GAPI_SRC_ = goog.string.Const.from( 'https://apis.google.com/js/client.js?onload=%{onload}'); /** * @const {string} * @private */ fireauth.RpcHandler.GAPI_CALLBACK_NAME_ = '__fcb' + Math.floor(Math.random() * 1000000).toString(); /** * Loads the GApi client library if it is not loaded. * @param {function()} callback The callback to invoke once it's loaded. * @param {function(?Object)} errback The error callback. * @private */ fireauth.RpcHandler.loadGApiJs_ = function(callback, errback) { // If gapi.client.request not available, load it dynamically. if (!((window['gapi'] || {})['client'] || {})['request']) { goog.global[fireauth.RpcHandler.GAPI_CALLBACK_NAME_] = function() { // Callback will be called by GApi, test properly loaded here instead of // after jsloader resolves. if (!((window['gapi'] || {})['client'] || {})['request']) { errback(new Error(fireauth.RpcHandler.ServerError.CORS_UNSUPPORTED)); } else { callback(); } }; var url = goog.html.TrustedResourceUrl.format( fireauth.RpcHandler.GAPI_SRC_, {'onload': fireauth.RpcHandler.GAPI_CALLBACK_NAME_}); // TODO: replace goog.net.jsloader with our own script includer. var result = goog.net.jsloader.safeLoad(url); result.addErrback(function() { // In case file fails to load. errback(new Error(fireauth.RpcHandler.ServerError.CORS_UNSUPPORTED)); }); } else { callback(); } }; /** * Sends XhrIo request using gapi.client. * @param {string} url The URL to make a request to. * @param {function(?Object)=} opt_callback The callback to run on completion. * @param {fireauth.RpcHandler.HttpMethod=} opt_httpMethod The HTTP send method. * @param {?ArrayBuffer|?ArrayBufferView|?Blob|?Document|?FormData|string=} * opt_data The request content. * @param {?Object=} opt_headers The request content headers. * @param {number=} opt_timeout The request timeout. * @private */ fireauth.RpcHandler.prototype.sendXhrUsingGApiClient_ = function( url, opt_callback, opt_httpMethod, opt_data, opt_headers, opt_timeout) { var self = this; // Wait for GApi dependency to load. fireauth.RpcHandler.loadGApi_.then(function() { window['gapi']['client']['setApiKey'](self.getApiKey()); // GApi maintains the Auth result and automatically append the Auth token to // all outgoing requests. Firebase Auth requests will be rejected if there // are others scopes (e.g. google plus) for the Auth token. Need to empty // the token before call gitkit api. Restored in callback. var oauth2Token = window['gapi']['auth']['getToken'](); window['gapi']['auth']['setToken'](null); window['gapi']['client']['request']({ 'path': url, 'method': opt_httpMethod, 'body': opt_data, 'headers': opt_headers, // This needs to be set to none, otherwise the access token will be passed // in the header field causing apiary to complain. 'authType': 'none', 'callback': function(response) { window['gapi']['auth']['setToken'](oauth2Token); if (opt_callback) { opt_callback(response); } } }); }).thenCatch(function(error) { // Catches failure to support CORS and propagates it. if (opt_callback) { // Simulate backend server error to be caught by upper layer. opt_callback({ 'error': { 'message': (error && error['message']) || fireauth.RpcHandler.ServerError.CORS_UNSUPPORTED } }); } }); }; /** * Validates the request for the STS access token. * * @param {?Object} data The STS token request body. * @return {boolean} Whether the request is valid. * @private */ fireauth.RpcHandler.prototype.validateStsTokenRequest_ = function(data) { if (data['grant_type'] == 'refresh_token' && data['refresh_token']) { // Exchange refresh token. return true; } else if (data['grant_type'] == 'authorization_code' && data['code']) { // Exchange ID token. return true; } else { // Invalid. return false; } }; /** * Handles the request for the STS access token. * * @param {!Object} data The STS token request body. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.requestStsToken = function(data) { var self = this; return new goog.Promise(function(resolve, reject) { if (self.validateStsTokenRequest_(data)) { self.sendXhr_( self.secureTokenEndpoint_ + '?key=' + encodeURIComponent(self.getApiKey()), function(response) { if (!response) { // An unparseable response from the XHR most likely indicates some // problem with the network. reject(new fireauth.AuthError( fireauth.authenum.Error.NETWORK_REQUEST_FAILED)); } else if (fireauth.RpcHandler.hasError_(response)) { reject(fireauth.RpcHandler.getDeveloperError_(response)); } else if ( !response[fireauth.RpcHandler.StsServerField.ACCESS_TOKEN] || !response[fireauth.RpcHandler.StsServerField.REFRESH_TOKEN]) { reject(new fireauth.AuthError( fireauth.authenum.Error.INTERNAL_ERROR)); } else { resolve(response); } }, fireauth.RpcHandler.HttpMethod.POST, goog.Uri.QueryData.createFromMap(data).toString(), self.secureTokenHeaders_, self.secureTokenTimeout_.get()); } else { reject(new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR)); } }); }; /** * @param {!Object} data The object to serialize. * @return {string} The serialized object with null, undefined and empty string * values removed. * @private */ fireauth.RpcHandler.serialize_ = function(data) { // goog.json.serialize converts undefined values to null. // This helper removes all empty strings, nulls and undefined from serialized // object. // Serialize trimmed data. return goog.json.serialize(fireauth.util.copyWithoutNullsOrUndefined(data)); }; /** * Creates and executes a request for the given API method using the legacy * Firebase Auth endpoint. * @param {string} method The API method. * @param {!fireauth.RpcHandler.HttpMethod} httpMethod The http request method. * @param {!Object} data The data for the API request. In the case of a GET * request, the contents of this object will be form encoded and appended * to the query string of the URL. No post body is sent in that case. If an * object value is specified, it will be converted to a string: * encodeURIComponent(String(value)). * @param {?fireauth.RpcHandler.ServerErrorMap=} opt_customErrorMap A map * of server error codes to client errors to override default error * handling. * @param {boolean=} opt_cachebuster Whether to append a unique string to * request to force backend to return an uncached response to request. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.requestFirebaseEndpoint = function( method, httpMethod, data, opt_customErrorMap, opt_cachebuster) { return this.requestAuthEndpoint_( this.firebaseEndpoint_, method, httpMethod, data, opt_customErrorMap, opt_cachebuster); }; /** * Creates and executes a request for the given API method using the identity * platform endpoint. * @param {string} method The API method. * @param {!fireauth.RpcHandler.HttpMethod} httpMethod The http request method. * @param {!Object} data The data for the API request. In the case of a GET * request, the contents of this object will be form encoded and appended * to the query string of the URL. No post body is sent in that case. If an * object value is specified, it will be converted to a string: * encodeURIComponent(String(value)). * @param {?fireauth.RpcHandler.ServerErrorMap=} opt_customErrorMap A map * of server error codes to client errors to override default error * handling. * @param {boolean=} opt_cachebuster Whether to append a unique string to * request to force backend to return an uncached response to request. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.requestIdentityPlatformEndpoint = function( method, httpMethod, data, opt_customErrorMap, opt_cachebuster) { return this.requestAuthEndpoint_( this.identityPlatformEndpoint_, method, httpMethod, data, opt_customErrorMap, opt_cachebuster); }; /** * Creates and executes a request for the given API method and Auth endpoint. * @param {string} endpoint The Auth endpoint to use. * @param {string} method The API method. * @param {!fireauth.RpcHandler.HttpMethod} httpMethod The http request method. * @param {!Object} data The data for the API request. In the case of a GET * request, the contents of this object will be form encoded and appended * to the query string of the URL. No post body is sent in that case. If an * object value is specified, it will be converted to a string: * encodeURIComponent(String(value)). * @param {?fireauth.RpcHandler.ServerErrorMap=} opt_customErrorMap A map * of server error codes to client errors to override default error * handling. * @param {boolean=} opt_cachebuster Whether to append a unique string to * request to force backend to return an uncached response to request. * @return {!goog.Promise<!Object>} * @private */ fireauth.RpcHandler.prototype.requestAuthEndpoint_ = function( endpoint, method, httpMethod, data, opt_customErrorMap, opt_cachebuster) { var self = this; // Construct endpoint URL. var uri = goog.Uri.parse(endpoint + method); uri.setParameterValue('key', this.getApiKey()); // Check whether to append cachebuster to request. if (opt_cachebuster) { uri.setParameterValue('cb', Date.now().toString()); } // Firebase allows GET endpoints. var isGet = httpMethod == fireauth.RpcHandler.HttpMethod.GET; if (isGet) { // For GET HTTP method, append data to query string. for (var key in data) { if (data.hasOwnProperty(key)) { uri.setParameterValue(key, data[key]); } } } return new goog.Promise(function(resolve, reject) { self.sendXhr_( uri.toString(), function(response) { if (!response) { // An unparseable response from the XHR most likely indicates some // problem with the network. reject(new fireauth.AuthError( fireauth.authenum.Error.NETWORK_REQUEST_FAILED)); } else if (fireauth.RpcHandler.hasError_(response)) { reject(fireauth.RpcHandler.getDeveloperError_(response, opt_customErrorMap || {})); } else { resolve(response); } }, httpMethod, // No post body data in GET requests. isGet ? undefined : fireauth.RpcHandler.serialize_(data), self.firebaseHeaders_, self.firebaseTimeout_.get()); }); }; /** * Verifies that the request has a valid email set. * @param {!Object} request * @private */ fireauth.RpcHandler.validateRequestHasEmail_ = function(request) { if (!fireauth.util.isValidEmailAddress(request['email'])) { throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_EMAIL); } }; /** * Verifies that the response has a valid email set. * @param {!Object} response * @private */ fireauth.RpcHandler.validateResponseHasEmail_ = function(response) { if (!fireauth.util.isValidEmailAddress(response['email'])) { throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR); } }; /** * Verifies that the an email is valid, if it is there. * @param {!Object} request * @private */ fireauth.RpcHandler.validateEmailIfPresent_ = function(request) { if ('email' in request) { fireauth.RpcHandler.validateRequestHasEmail_(request); } }; /** * @param {string} providerId The provider ID. * @param {?Array<string>=} opt_additionalScopes The list of scope strings. * @return {?string} The IDP and its comma separated scope strings serialized. * @private */ fireauth.RpcHandler.getAdditionalScopes_ = function(providerId, opt_additionalScopes) { var scopes = {}; if (opt_additionalScopes && opt_additionalScopes.length) { scopes[providerId] = opt_additionalScopes.join(','); // Return stringified scopes. return goog.json.serialize(scopes); } return null; }; /** * Validates a response from getAuthUri. * @param {?Object} response The getAuthUri response data. * @private */ fireauth.RpcHandler.validateGetAuthResponse_ = function(response) { if (!response[fireauth.RpcHandler.AuthServerField.AUTH_URI]) { throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR, 'Unable to determine the authorization endpoint for the specified '+ 'provider. This may be an issue in the provider configuration.'); } else if ( !response[fireauth.RpcHandler.AuthServerField.SESSION_ID]) { throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR); } }; /** * Requests createAuthUri endpoint to retrieve the authUri and session ID for * the start of an OAuth handshake. * @param {string} providerId The provider ID. * @param {string} continueUri The IdP callback URL. * @param {?Object=} opt_customParameters The optional OAuth custom parameters * plain object. * @param {?Array<string>=} opt_additionalScopes The list of scope strings. * @param {?string=} opt_email The optional email. * @param {?string=} opt_sessionId The optional session ID. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.getAuthUri = function( providerId, continueUri, opt_customParameters, opt_additionalScopes, opt_email, opt_sessionId) { // SAML provider request is constructed differently than OAuth requests. var isSaml = fireauth.idp.isSaml(providerId); var request = { 'identifier': opt_email, 'providerId': providerId, 'continueUri': continueUri, 'customParameter': opt_customParameters || {}, 'oauthScope': fireauth.RpcHandler.getAdditionalScopes_( providerId, opt_additionalScopes), 'sessionId': opt_sessionId }; // Custom parameters and OAuth scopes should be ignored. if (isSaml) { delete request['customParameter']; delete request['oauthScope']; } // When sessionId is provided, mobile flow (Cordova) is being used, force // code flow and not implicit flow. All other providers use code flow by // default. if (opt_sessionId && providerId == fireauth.idp.ProviderId.GOOGLE) { request['authFlowType'] = 'CODE_FLOW'; } return this.invokeRpc(fireauth.RpcHandler.ApiMethod.GET_AUTH_URI, request); }; /** * Gets the list of IDPs that can be used to log in for the given identifier. * @param {string} identifier The identifier, such as an email address. * @return {!goog.Promise<!Array<string>>} */ fireauth.RpcHandler.prototype.fetchProvidersForIdentifier = function(identifier) { // createAuthUri returns an error if continue URI is not http or https. // For environments like Cordova, Chrome extensions, native frameworks, file // systems, etc, use http://localhost as continue URL. var continueUri = fireauth.util.isHttpOrHttps() ? fireauth.util.getCurrentUrl() : 'http://localhost'; var request = { 'identifier': identifier, 'continueUri': continueUri }; return this.invokeRpc(fireauth.RpcHandler.ApiMethod.CREATE_AUTH_URI, request) .then(function(response) { return response[fireauth.RpcHandler.AuthServerField.ALL_PROVIDERS] || []; }); }; /** * Returns the list of sign in methods for the given identifier. * @param {string} identifier The identifier, such as an email address. * @return {!goog.Promise<!Array<string>>} */ fireauth.RpcHandler.prototype.fetchSignInMethodsForIdentifier = function( identifier) { // createAuthUri returns an error if continue URI is not http or https. // For environments like Cordova, Chrome extensions, native frameworks, file // systems, etc, use http://localhost as continue URL. var continueUri = fireauth.util.isHttpOrHttps() ? fireauth.util.getCurrentUrl() : 'http://localhost'; var request = { 'identifier': identifier, 'continueUri': continueUri }; return this.invokeRpc(fireauth.RpcHandler.ApiMethod.CREATE_AUTH_URI, request) .then(function(response) { return response[fireauth.RpcHandler.AuthServerField.SIGNIN_METHODS] || []; }); }; /** * Gets the list of authorized domains for the specified project. * @return {!goog.Promise<!Array<string>>} */ fireauth.RpcHandler.prototype.getAuthorizedDomains = function() { return this.invokeRpc(fireauth.RpcHandler.ApiMethod.GET_PROJECT_CONFIG, {}) .then(function(response) { return response[ fireauth.RpcHandler.AuthServerField.AUTHORIZED_DOMAINS] || []; }); }; /** * Gets the reCAPTCHA parameters needed to render the project's provisioned * reCAPTCHA. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.getRecaptchaParam = function() { return this.invokeRpc(fireauth.RpcHandler.ApiMethod.GET_RECAPTCHA_PARAM, {}); }; /** * Gets the list of authorized domains for the specified project. * @return {!goog.Promise<string>} */ fireauth.RpcHandler.prototype.getDynamicLinkDomain = function() { var request = { 'returnDynamicLink': true }; return this.invokeRpc( fireauth.RpcHandler.ApiMethod.RETURN_DYNAMIC_LINK, request); }; /** * Checks if the provided iOS bundle ID belongs to the project as specified by * the API key. * @param {string} iosBundleId The iOS bundle ID to check. * @return {!goog.Promise<void>} */ fireauth.RpcHandler.prototype.isIosBundleIdValid = function(iosBundleId) { var request = { 'iosBundleId': iosBundleId }; // This will either resolve if the identifier is valid or throw INVALID_APP_ID // if not. return this.invokeRpc( fireauth.RpcHandler.ApiMethod.GET_PROJECT_CONFIG, request) .then(function(result) { // Do not return anything. }); }; /** * Checks if the provided Android package name belongs to the project as * specified by the API key. * @param {string} androidPackageName The iOS bundle ID to check. * @param {?string=} opt_sha1Cert The optional SHA-1 Android cert to check. * @return {!goog.Promise<void>} */ fireauth.RpcHandler.prototype.isAndroidPackageNameValid = function(androidPackageName, opt_sha1Cert) { var request = { 'androidPackageName': androidPackageName }; // This is relevant for the native Android SDK flow. // This will redirect to an FDL domain owned by GMScore instead of // the developer's FDL domain as is done for Cordova apps. if (!!opt_sha1Cert) { request['sha1Cert'] = opt_sha1Cert; } // When no sha1Cert is passed, this will either resolve if the identifier is // valid or throw INVALID_APP_ID if not. // When sha1Cert is also passed, this will either resolve or fail with an // INVALID_CERT_HASH error. return this.invokeRpc( fireauth.RpcHandler.ApiMethod.GET_PROJECT_CONFIG, request) .then(function(result) { // Do not return anything. }); }; /** * Checks if the provided OAuth client ID belongs to the project as specified by * the API key. * @param {string} clientId The OAuth client ID to check. * @return {!goog.Promise<void>} */ fireauth.RpcHandler.prototype.isOAuthClientIdValid = function(clientId) { var request = { 'clientId': clientId }; // This will either resolve if the client ID is valid or throw // INVALID_OAUTH_CLIENT_ID if not. return this.invokeRpc( fireauth.RpcHandler.ApiMethod.GET_PROJECT_CONFIG, request) .then(function(result) { // Do not return anything. }); }; /** * Requests getAccountInfo endpoint using an ID token. * @param {string} idToken The ID token. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.getAccountInfoByIdToken = function(idToken) { var request = {'idToken': idToken}; return this.invokeRpc(fireauth.RpcHandler.ApiMethod.GET_ACCOUNT_INFO, request); }; /** * Validates a request to sign in with email and password. * @param {!Object} request * @private */ fireauth.RpcHandler.validateVerifyCustomTokenRequest_ = function(request) { if (!request['token']) { throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_CUSTOM_TOKEN); } }; /** * Verifies a custom token and returns a Promise that resolves with the ID * token. * @param {string} token The custom token. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.verifyCustomToken = function(token) { var request = {'token': token}; return this.invokeRpc(fireauth.RpcHandler.ApiMethod.VERIFY_CUSTOM_TOKEN, request); }; /** * Validates a request to sign in with email and password. * @param {!Object} request * @private */ fireauth.RpcHandler.validateVerifyPasswordRequest_ = function(request) { fireauth.RpcHandler.validateRequestHasEmail_(request); if (!request['password']) { throw new fireauth.AuthError(fireauth.authenum.Error.INVALID_PASSWORD); } }; /** * Verifies a password and returns a Promise that resolves with the ID * token. * @param {string} email The email address. * @param {string} password The entered password. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.verifyPassword = function(email, password) { var request = { 'email': email, 'password': password }; return this.invokeRpc(fireauth.RpcHandler.ApiMethod.VERIFY_PASSWORD, request); }; /** * Verifies an email link OTP for sign-in and returns a Promise that resolves * with the ID token. * @param {string} email The email address. * @param {string} oobCode The email action OTP. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.emailLinkSignIn = function(email, oobCode) { var request = { 'email': email, 'oobCode': oobCode }; return this.invokeRpc( fireauth.RpcHandler.ApiMethod.EMAIL_LINK_SIGNIN, request); }; /** * Verifies an email link OTP for linking and returns a Promise that resolves * with the ID token. * @param {string} idToken The ID token. * @param {string} email The email address. * @param {string} oobCode The email action OTP. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.emailLinkSignInForLinking = function(idToken, email, oobCode) { var request = { 'idToken': idToken, 'email': email, 'oobCode': oobCode }; return this.invokeRpc( fireauth.RpcHandler.ApiMethod.EMAIL_LINK_SIGNIN_FOR_LINKING, request); }; /** * Validates a response that should contain an ID token. * If no ID token is available, it checks if a multi-factor pending credential * is available instead. In that case, it throws the MFA_REQUIRED error code. * @param {?Object} response The server response data. * @private */ fireauth.RpcHandler.validateIdTokenResponse_ = function(response) { if (!response[fireauth.RpcHandler.AuthServerField.ID_TOKEN]) { // User could be a second factor user. // When second factor is required, a pending credential is returned. if (response[fireauth.RpcHandler.AuthServerField.MFA_PENDING_CREDENTIAL]) { throw new fireauth.AuthError( fireauth.authenum.Error.MFA_REQUIRED, null, goog.object.clone(response)); } throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR); } }; /** * Validates a getRecaptchaParam response. * @param {?Object} response The server response data. * @private */ fireauth.RpcHandler.validateGetRecaptchaParamResponse_ = function(response) { // Both are required. This could change though. if (!response[fireauth.RpcHandler.AuthServerField.RECAPTCHA_SITE_KEY]) { throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR); } }; /** * Validates a request that sends the verification ID and code for a sign in/up * phone Auth flow. * @param {!Object} request The server request object. * @private */ fireauth.RpcHandler.validateVerifyPhoneNumberRequest_ = function(request) { // There are 2 cases here: // case 1: sessionInfo and code // case 2: phoneNumber and temporaryProof if (request['phoneNumber'] || request['temporaryProof']) { // Case 2. Both phoneNumber and temporaryProof should be set. if (!request['phoneNumber'] || !request['temporaryProof']) { throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR); } } else { // Otherwise it's case 1, so we expect sessionInfo and code. if (!request['sessionInfo']) { throw new fireauth.AuthError( fireauth.authenum.Error.MISSING_SESSION_INFO); } if (!request['code']) { throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_CODE); } } }; /** * Validates a request that sends the verification ID and code for a link/update * phone Auth flow. * @param {!Object} request The server request object. * @private */ fireauth.RpcHandler.validateVerifyPhoneNumberLinkRequest_ = function(request) { // idToken should be required here. if (!request['idToken']) { throw new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR); } // The other request parameters match the sign in flow. fireauth.RpcHandler.validateVerifyPhoneNumberRequest_(request); }; /** * Validates a request to create an email and password account. * @param {!Object} request * @private */ fireauth.RpcHandler.validateCreateAccountRequest_ = function(request) { fireauth.RpcHandler.validateRequestHasEmail_(request); if (!request['password']) { throw new fireauth.AuthError(fireauth.authenum.Error.WEAK_PASSWORD); } }; /** * Validates a request to createAuthUri. * @param {!Object} request * @private */ fireauth.RpcHandler.validateGetAuthUriRequest_ = function(request) { if (!request['continueUri']) { throw new fireauth.AuthError(fireauth.authenum.Error.MISSING_CONTINUE_URI); } // Either a SAML or non SAML providerId must be provided. if (!request['providerId']) { throw new fireauth.AuthError( fireauth.authenum.Error.INTERNAL_ERROR, 'A provider ID must be provided in the request.'); } }; /** * Creates an email/password account. Returns a Promise that resolves with the * ID token. * @param {string} email The email address of the account. * @param {string} password The password. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.createAccount = function(email, password) { var request = { 'email': email, 'password': password }; return this.invokeRpc(fireauth.RpcHandler.ApiMethod.CREATE_ACCOUNT, request); }; /** * Signs in a user as anonymous. Returns a Promise that resolves with the * ID token. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.signInAnonymously = function() { return this.invokeRpc(fireauth.RpcHandler.ApiMethod.SIGN_IN_ANONYMOUSLY, {}); }; /** * Deletes the user's account corresponding to the idToken given. * @param {string} idToken The idToken of the user. * @return {!goog.Promise<undefined>} */ fireauth.RpcHandler.prototype.deleteAccount = function(idToken) { var request = { 'idToken': idToken }; return this.invokeRpc(fireauth.RpcHandler.ApiMethod.DELETE_ACCOUNT, request); }; /** * Requests setAccountInfo endpoint for updateEmail operation. * @param {string} idToken The ID token. * @param {string} newEmail The new email. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.updateEmail = function(idToken, newEmail) { var request = { 'idToken': idToken, 'email': newEmail }; return this.invokeRpc(fireauth.RpcHandler.ApiMethod.SET_ACCOUNT_INFO, request); }; /** * Validates a setAccountInfo request that updates the password. * @param {!Object} request * @private */ fireauth.RpcHandler.validateSetAccountInfoSensitive_ = function(request) { fireauth.RpcHandler.validateEmailIfPresent_(request); if (!request['password']) { throw new fireauth.AuthError(fireauth.authenum.Error.WEAK_PASSWORD); } }; /** * Requests setAccountInfo endpoint for updatePassword operation. * @param {string} idToken The ID token. * @param {string} newPassword The new password. * @return {!goog.Promise<!Object>} */ fireauth.RpcHandler.prototype.updatePassword = function(idToken, newPassword) { var request = { 'idToken': idToken, 'password': newPassword }; return this