/* * Copyright 2016 Google Inc. All Rights Reserved. * * 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 all configurations used by FirebaseUI widget. */ goog.module('firebaseui.auth.widget.Config'); goog.module.declareLegacyNamespace(); const AuthConfig = goog.require('firebaseui.auth.Config'); const AuthUIError = goog.require('firebaseui.auth.AuthUIError'); const PhoneNumber = goog.require('firebaseui.auth.PhoneNumber'); const Uri = goog.require('goog.Uri'); const country = goog.require('firebaseui.auth.data.country'); const googArray = goog.require('goog.array'); const googObject = goog.require('goog.object'); const idp = goog.require('firebaseui.auth.idp'); const log = goog.require('firebaseui.auth.log'); const util = goog.require('firebaseui.auth.util'); const utils = goog.require('goog.uri.utils'); /** Application configuration settings. */ class Config { constructor() { /** @const @private {!AuthConfig} The AuthUI config object. */ this.config_ = new AuthConfig(); this.config_.define('autoUpgradeAnonymousUsers'); this.config_.define('callbacks'); /** * Determines which credential helper to use. By default, * no credentialHelper is selected. */ this.config_.define( 'credentialHelper', Config.CredentialHelper.NONE); /** * Determines whether to immediately redirect to the provider's site or * instead show the default 'Sign in with Provider' button when there * is only a single federated provider in signInOptions. In order for this * option to take effect, the signInOptions must only hold a single * federated provider (like 'google.com') and signInFlow must be set to * 'redirect'. */ this.config_.define('immediateFederatedRedirect', false); this.config_.define('popupMode', false); this.config_.define('privacyPolicyUrl'); /** Determines the redirect URL query key. */ this.config_.define( 'queryParameterForSignInSuccessUrl', 'signInSuccessUrl'); this.config_.define('queryParameterForWidgetMode', 'mode'); /** * Determines the sign-in flow, 'popup' or 'redirect'. The former will use * signInWithPopup where as the latter will use the default * signInWithRedirect when a federated sign-in is triggered. */ this.config_.define('signInFlow'); /** * Determines the list of IdPs for handling federated sign-in as well as * password account sign-up explicitly. The developer can also request * additional scopes. */ this.config_.define('signInOptions'); this.config_.define('signInSuccessUrl'); this.config_.define('siteName'); this.config_.define('tosUrl'); this.config_.define('widgetUrl'); /** * The configuration mirroring the project user actions ("Enable create") * settings. When sign-up is disabled in the project settings, this * configuration should be provided with the status field set to `true`. * This does not enforce the policy but is rather useful for providing * additional instructions to the end user when a user tries to create a * new user account and the Auth server blocks the operation. */ this.config_.define('adminRestrictedOperation'); } /** * Gets the widget URL for a specific mode. * The 'widgetUrl' configuration is required for this method. * @param {?Config.WidgetMode=} mode The mode for the widget. * @return {string} The URL of the callback widget. */ getRequiredWidgetUrl(mode = undefined) { const url = /** @type {string} */ (this.config_.getRequired('widgetUrl')); return this.widgetUrlForMode_(url, mode); } /** * Gets the widget URL for a specific mode. * If the 'widgetUrl' configuration is not set, current URL is used as the * base URL for the widget. * @param {?Config.WidgetMode=} mode The mode for the widget. * @return {string} The URL of the callback widget. */ getWidgetUrl(mode = undefined) { const url = /** @type {string|undefined} */ ( this.config_.get('widgetUrl')) || // If no widget URL is provided, use the current one. util.getCurrentUrl(); return this.widgetUrlForMode_(url, mode); } /** * Gets the callback URL for IdP. It always returns an absolute URL. * @return {string} The callback URL. */ getIdpCallbackUrl() { return Uri.resolve( window.location.href, this.getWidgetUrl()).toString(); } /** * @param {string} baseUrl The base URL of the widget. * @param {?Config.WidgetMode=} mode The mode for the widget. * @return {string} The URL of the widget for a specific mode. * @private */ widgetUrlForMode_(baseUrl, mode = undefined) { if (mode) { const key = this.getQueryParameterForWidgetMode(); return utils.setParam(baseUrl, key, mode); } else { return baseUrl; } } /** @return {string} The sign-in URL of the site. */ getSignInSuccessUrl() { return /** @type {string} */ (this.config_.get('signInSuccessUrl')); } /** @return {boolean} Whether to auto upgrade anonymous users. */ autoUpgradeAnonymousUsers() { const autoUpgradeAnonymousUsers = !!this.config_.get('autoUpgradeAnonymousUsers'); // Confirm signInFailure callback is provided when anonymous upgrade is // enabled. This is required to provide a means of recovery for merge // conflict flows. if (autoUpgradeAnonymousUsers && !this.getSignInFailureCallback()) { log.error('Missing "signInFailure" callback: ' + '"signInFailure" callback needs to be provided when ' + '"autoUpgradeAnonymousUsers" is set to true.'); } return autoUpgradeAnonymousUsers; } /** * Returns the normalized list of valid user-enabled IdPs. * The user may specify each IdP as just a provider ID or as an object * containing provider ID and additional scopes; this method converts all * entries to the object format and filters out entries with invalid * providers. * @return {!Array} The normalized sign-in options. * @private */ getSignInOptions_() { const signInOptions = this.config_.get('signInOptions') || []; const normalizedOptions = []; for (let i = 0; i < signInOptions.length; i++) { const providerConfig = signInOptions[i]; // If the config is not in object format, convert to object format. const normalizedConfig = goog.isObject(providerConfig) ? providerConfig : {'provider': providerConfig}; if (normalizedConfig['provider']) { normalizedOptions.push(normalizedConfig); } } return normalizedOptions; } /** * Returns the normalized signInOptions for the specified provider. * @param {string} providerId The provider id whose signInOptions are to be * returned. * @return {?Object} The normalized sign-in options for the specified * provider. * @private */ getSignInOptionsForProvider_(providerId) { const signInOptions = this.getSignInOptions_(); // For each sign-in option. for (let i = 0; i < signInOptions.length; i++) { // Check if current option matches provider ID. if (signInOptions[i]['provider'] === providerId) { return signInOptions[i]; } } return null; } /** * @return {!Array} The list of supported IdPs including password * special IdP. */ getProviders() { return this.getSignInOptions_().map(option => option['provider']); } /** * @param {string} providerId The provider id whose sign in provider config * is to be returned. * @return {?Config.ProviderConfig} The list of sign in provider configs for * supported IdPs. */ getConfigForProvider(providerId) { const providerConfigs = this.getProviderConfigs(); for (let i = 0; i < providerConfigs.length; i++) { // Check if current option matches provider ID. if (providerConfigs[i]['providerId'] === providerId) { return providerConfigs[i]; } } return null; } /** * Returns all available provider configs. For built-in providers, provider * display name, button color and icon URL are fixed and cannot be overridden. * @return {!Array} The list of supported IdP configs. */ getProviderConfigs() { return this.getSignInOptions_().map(option => { if (idp.isSupportedProvider(option['provider']) || googArray.contains(UI_SUPPORTED_PROVIDERS, option['provider'])) { // The login hint key is also automatically set for built-in providers // that support it. const providerConfig = { providerId: option['provider'], // Since developers may be using // G-Suite for Google sign in or // want to label email/password as their own provider, we should // allow customization of these attributes. providerName: option['providerName'] || null, fullLabel: option['fullLabel'] || null, buttonColor: option['buttonColor'] || null, iconUrl: option['iconUrl'] ? util.sanitizeUrl(option['iconUrl']) : null }; for (const key in providerConfig) { if (providerConfig[key] === null) { delete providerConfig[key]; } } return providerConfig; } else { return { providerId: option['provider'], providerName: option['providerName'] || null, fullLabel: option['fullLabel'] || null, buttonColor: option['buttonColor'] || null, iconUrl: option['iconUrl'] ? util.sanitizeUrl(option['iconUrl']) : null, loginHintKey: option['loginHintKey'] || null }; } }); } /** * @return {?string} The googleyolo configuration client ID if available. */ getGoogleYoloClientId() { const signInOptions = this.getSignInOptionsForProvider_( firebase.auth.GoogleAuthProvider.PROVIDER_ID); if (signInOptions && signInOptions['clientId'] && this.getCredentialHelper() === Config.CredentialHelper.GOOGLE_YOLO) { return signInOptions['clientId'] || null; } return null; } /** * @return {boolean} Whether the user is disabled to sign up with email. */ isEmailSignUpDisabled() { // This is only applicable to email. const emailSignInOption = this.getSignInOptionsForProvider_( firebase.auth.EmailAuthProvider.PROVIDER_ID); return !!(emailSignInOption && emailSignInOption['disableSignUp'] && emailSignInOption['disableSignUp']['status']); } /** * @return {boolean} Whether user sign up is admin restricted. */ isAdminRestrictedOperationConfigured() { const adminRestrictedOperation = this.config_.get('adminRestrictedOperation') || null; return !!(adminRestrictedOperation && adminRestrictedOperation['status']); } /** * @return {boolean} Whether the user should be prompted to select an * account. */ isAccountSelectionPromptEnabled() { // This is only applicable to Google. If prompt is set, googleyolo retrieve // is disabled. Auto sign-in should be manually disabled. // Get Google custom parameters. const googleCustomParameters = this.getProviderCustomParameters( firebase.auth.GoogleAuthProvider.PROVIDER_ID); // Google custom parameters must have prompt set to select_account, // otherwise account selection prompt is considered disabled. return !!(googleCustomParameters && googleCustomParameters['prompt'] === 'select_account'); } /** * Returns the corresponding Firebase Auth provider ID for the googleyolo * authMethod provided. * @param {?string} authMethod The googleyolo authMethod whose corresponding * Firebase provider ID is to be returned. * @return {?string} The corresponding Firebase provider ID if available. */ getProviderIdFromAuthMethod(authMethod) { let providerId = null; // For each supported provider. this.getSignInOptions_().forEach(option => { // Check for matching authMethod. if (option['authMethod'] === authMethod) { // Get the providerId for that provider. providerId = option['provider']; } }); // Return the corresponding provider ID. return providerId; } /** * @return {?Object} The filtered reCAPTCHA parameters used when * phone auth provider is enabled. If none provided, null is returned. */ getRecaptchaParameters() { let recaptchaParameters = null; this.getSignInOptions_().forEach(option => { if (option['provider'] == firebase.auth.PhoneAuthProvider.PROVIDER_ID && goog.isObject( option['recaptchaParameters']) && // Confirm valid object. !Array.isArray(option['recaptchaParameters'])) { // Clone original object. recaptchaParameters = googObject.clone(option['recaptchaParameters']); } }); if (recaptchaParameters) { // Keep track of all blacklisted keys passed by the developer. const blacklistedKeys = []; // Go over all blacklisted keys and remove them from the original object. BLACKLISTED_RECAPTCHA_KEYS.forEach(key => { if (typeof recaptchaParameters[key] !== 'undefined') { blacklistedKeys.push(key); delete recaptchaParameters[key]; } }); // Log a warning for invalid keys. // This will show on each call. if (blacklistedKeys.length) { log.warning( 'The following provided "recaptchaParameters" keys are not ' + 'allowed: ' + blacklistedKeys.join(', ')); } } return recaptchaParameters; } /** * @return {?string} The admin contact email when users are restricted from * sign up. Otherwise, null is returned. */ getAdminRestrictedOperationAdminEmail() { const adminRestrictedOperation = this.config_.get('adminRestrictedOperation'); if (adminRestrictedOperation && adminRestrictedOperation['adminEmail']) { return /** @type {string} */(adminRestrictedOperation['adminEmail']); } return null; } /** * @return {?function()} The callback to redirect to the help link when users * are restricted from sign up. Otherwise, null is returned. */ getAdminRestrictedOperationHelpLinkCallback() { const adminRestrictedOperation = this.config_.get('adminRestrictedOperation') || null; if (adminRestrictedOperation) { const helpLink = adminRestrictedOperation['helpLink'] || null; if (helpLink && typeof helpLink === 'string') { return () => { util.open( /** @type {string} */ (helpLink), util.isCordovaInAppBrowserInstalled() ? '_system' : '_blank'); }; } } return null; } /** * @return {?string} The admin contact email when sign up is disabled. */ getEmailProviderAdminEmail() { const emailSignInOption = this.getSignInOptionsForProvider_( firebase.auth.EmailAuthProvider.PROVIDER_ID); return (emailSignInOption && emailSignInOption['disableSignUp'] && emailSignInOption['disableSignUp']['adminEmail']) || null; } /** * @return {?function()} The callback to redirect to the help link when email * provider sign up is disabled. */ getEmailProviderHelpLinkCallBack() { const emailSignInOption = this.getSignInOptionsForProvider_( firebase.auth.EmailAuthProvider.PROVIDER_ID); if (emailSignInOption && emailSignInOption['disableSignUp']) { const helpLink = emailSignInOption['disableSignUp']['helpLink'] || null; if (helpLink && typeof helpLink === 'string') { return () => { util.open( /** @type {string} */ (helpLink), util.isCordovaInAppBrowserInstalled() ? '_system' : '_blank'); }; } } return null; } /** * @param {string} providerId The provider id whose additional scopes are to * be returned. * @return {!Array} The list of additional scopes for specified * provider. */ getProviderAdditionalScopes(providerId) { // Get provided sign-in options for specified provider. const signInOptions = this.getSignInOptionsForProvider_(providerId); const scopes = signInOptions && signInOptions['scopes']; return Array.isArray(scopes) ? scopes : []; } /** * @param {string} providerId The provider id whose custom parameters are to * be returned. * @return {?Object} The custom parameters for the current provider. */ getProviderCustomParameters(providerId) { // Get provided sign-in options for specified provider. const signInOptions = this.getSignInOptionsForProvider_(providerId); // Get customParameters for that provider if available. const customParameters = signInOptions && signInOptions['customParameters']; // Custom parameters must be an object. if (goog.isObject(customParameters)) { // Clone original custom parameters. const clonedCustomParameters = googObject.clone(customParameters); // Delete login_hint from Google provider as it could break the flow. if (providerId === firebase.auth.GoogleAuthProvider.PROVIDER_ID) { delete clonedCustomParameters['login_hint']; } // Delete login from GitHub provider as it could break the flow. if (providerId === firebase.auth.GithubAuthProvider.PROVIDER_ID) { delete clonedCustomParameters['login']; } return clonedCustomParameters; } return null; } /** * Returns the default country to select for phone authentication. * @return {?string} The default naional number, or null if phone auth is not * enabled. */ getPhoneAuthDefaultNationalNumber() { const signInOptions = this.getSignInOptionsForProvider_( firebase.auth.PhoneAuthProvider.PROVIDER_ID); // Check if loginHint passed. If so, get the national number from there. // If no defaultNationalNumber passed, use this value instead. let defaultPhoneNumber = null; if (signInOptions && typeof (signInOptions['loginHint']) === 'string') { defaultPhoneNumber = PhoneNumber.fromString( /** @type {string} */ (signInOptions['loginHint'])); } return (signInOptions && signInOptions['defaultNationalNumber']) || (defaultPhoneNumber && defaultPhoneNumber.nationalNumber) || null; } /** * Returns the default country to select for phone authentication. * @return {?country.Country} The default country, or null if phone auth is * not enabled or the country is not found. */ getPhoneAuthDefaultCountry() { const signInOptions = this.getSignInOptionsForProvider_( firebase.auth.PhoneAuthProvider.PROVIDER_ID); const iso2 = signInOptions && signInOptions['defaultCountry'] || null; const countries = iso2 && country.getCountriesByIso2(iso2); // Check if loginHint passed. If so, get the country ID from there. // If no defaultCountry passed, use this value instead. let defaultPhoneNumber = null; if (signInOptions && typeof (signInOptions['loginHint']) === 'string') { defaultPhoneNumber = PhoneNumber.fromString( /** @type {string} */ (signInOptions['loginHint'])); } // If there are multiple entries, pick the first one. return (countries && countries[0]) || (defaultPhoneNumber && defaultPhoneNumber.getCountry()) || null; } /** * Returns the available countries for phone authentication. * @return {?Array} The available country list, or null if * phone Auth is not enabled. */ getPhoneAuthAvailableCountries() { const signInOptions = this.getSignInOptionsForProvider_( firebase.auth.PhoneAuthProvider.PROVIDER_ID); if (!signInOptions) { return null; } const whitelistedCountries = signInOptions['whitelistedCountries']; const blacklistedCountries = signInOptions['blacklistedCountries']; // First validate the input. if (typeof whitelistedCountries !== 'undefined' && (!Array.isArray(whitelistedCountries) || whitelistedCountries.length == 0)) { throw new Error('WhitelistedCountries must be a non-empty array.'); } if (typeof blacklistedCountries !== 'undefined' && (!Array.isArray(blacklistedCountries))) { throw new Error('BlacklistedCountries must be an array.'); } // If both allowlist and disallowlist are provided, throw error. if (whitelistedCountries && blacklistedCountries) { throw new Error( 'Both whitelistedCountries and blacklistedCountries are provided.'); } // If no allowlist or disallowlist provided, return all available countries. if (!whitelistedCountries && !blacklistedCountries) { return country.COUNTRY_LIST; } let countries = []; const availableCountries = []; if (whitelistedCountries) { // Allowlist is provided. const whitelistedCountryMap = {}; for (let i = 0; i < whitelistedCountries.length; i++) { countries = country .getCountriesByE164OrIsoCode(whitelistedCountries[i]); // Remove duplicate and overlaps by putting into a map. for (let j = 0; j < countries.length; j++) { whitelistedCountryMap[countries[j].e164_key] = countries[j]; } } for (let countryKey in whitelistedCountryMap) { if (whitelistedCountryMap.hasOwnProperty(countryKey)) { availableCountries.push(whitelistedCountryMap[countryKey]); } } return availableCountries; } else { const blacklistedCountryMap = {}; for (let i = 0; i < blacklistedCountries.length; i++) { countries = country .getCountriesByE164OrIsoCode(blacklistedCountries[i]); // Remove duplicate and overlaps by putting into a map. for (let j = 0; j < countries.length; j++) { blacklistedCountryMap[countries[j].e164_key] = countries[j]; } } for (let k = 0; k < country.COUNTRY_LIST.length; k++) { if (!googObject.containsKey( blacklistedCountryMap, country.COUNTRY_LIST[k].e164_key)) { availableCountries.push(country.COUNTRY_LIST[k]); } } return availableCountries; } } /** @return {string} The query parameter name for widget mode. */ getQueryParameterForWidgetMode() { return /** @type {string} */ ( this.config_.getRequired('queryParameterForWidgetMode')); } /** @return {string} The redirect URL query parameter. */ getQueryParameterForSignInSuccessUrl() { return /** @type {string} */ ( this.config_.getRequired('queryParameterForSignInSuccessUrl')); } /** @return {string} The name of the website. */ getSiteName() { return /** @type {string} */ (this.config_.getRequired('siteName')); } /** * @return {?function()} The ToS callback for the site. If URL is provided, * wraps the URL with a callback function. */ getTosUrl() { const tosUrl = this.config_.get('tosUrl') || null; const privacyPolicyUrl = this.config_.get('privacyPolicyUrl') || null; if (tosUrl && !privacyPolicyUrl) { log.warning( 'Privacy Policy URL is missing, the link will not be displayed.'); } if (tosUrl && privacyPolicyUrl) { if (typeof tosUrl === 'function') { return /** @type {function()} */ (tosUrl); } else if (typeof tosUrl === 'string') { return () => { util.open( /** @type {string} */ (tosUrl), util.isCordovaInAppBrowserInstalled() ? '_system' : '_blank'); }; } } return null; } /** * @return {?function()} The Privacy Policy callback for the site. If URL is * provided, wraps the URL with a callback function. */ getPrivacyPolicyUrl() { const tosUrl = this.config_.get('tosUrl') || null; const privacyPolicyUrl = this.config_.get('privacyPolicyUrl') || null; if (privacyPolicyUrl && !tosUrl) { log.warning( 'Term of Service URL is missing, the link will not be displayed.'); } if (tosUrl && privacyPolicyUrl) { if (typeof privacyPolicyUrl === 'function') { return /** @type {function()} */ (privacyPolicyUrl); } else if (typeof privacyPolicyUrl === 'string') { return () => { util.open( /** @type {string} */ (privacyPolicyUrl), util.isCordovaInAppBrowserInstalled() ? '_system' : '_blank'); }; } } return null; } /** * @return {boolean} Whether the display name should be displayed. Defaults * to true. */ isDisplayNameRequired() { // Get provided sign-in options for specified provider. const signInOptions = this.getSignInOptionsForProvider_( firebase.auth.EmailAuthProvider.PROVIDER_ID); if (signInOptions && typeof signInOptions['requireDisplayName'] !== 'undefined') { return /** @type {boolean} */ (!!signInOptions['requireDisplayName']); } return true; } /** * @return {boolean} Whether email link sign-in is allowed. Defaults to false. */ isEmailLinkSignInAllowed() { // Get provided sign-in options for specified provider. const signInOptions = this.getSignInOptionsForProvider_( firebase.auth.EmailAuthProvider.PROVIDER_ID); return !!(signInOptions && signInOptions['signInMethod'] === firebase.auth.EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD); } /** * @return {boolean} Whether password sign-in is allowed. Defaults to true. */ isEmailPasswordSignInAllowed() { return !this.isEmailLinkSignInAllowed(); } /** @return {boolean} Whether same device is forced for email link sign-in. */ isEmailLinkSameDeviceForced() { // Get provided sign-in options for specified provider. const signInOptions = this.getSignInOptionsForProvider_( firebase.auth.EmailAuthProvider.PROVIDER_ID); return !!(signInOptions && signInOptions['forceSameDevice']); } /** * @return {?firebase.auth.ActionCodeSettings} The ActionCodeSettings if * email link sign-in is enabled. Null is returned otherwise. */ getEmailLinkSignInActionCodeSettings() { if (this.isEmailLinkSignInAllowed()) { const actionCodeSettings = { 'url': util.getCurrentUrl(), 'handleCodeInApp': true, }; // Get provided sign-in options for specified provider. const signInOptions = this.getSignInOptionsForProvider_( firebase.auth.EmailAuthProvider.PROVIDER_ID); if (signInOptions && typeof signInOptions['emailLinkSignIn'] === 'function') { googObject.extend( actionCodeSettings, signInOptions['emailLinkSignIn']()); } // URL could be provided using a relative path. actionCodeSettings['url'] = Uri.resolve( util.getCurrentUrl(), actionCodeSettings['url']).toString(); return actionCodeSettings; } return null; } /** @return {boolean} Whether to prefer popup mode. */ getPopupMode() { return !!this.config_.get('popupMode'); } /** * Determines whether to show the 'nascar' sign-in buttons screen or * immediately redirect to the provider's site when there is only a single * federated provider in signInOptions. In order for this option to take * effect, the signInOptions must only hold a single federated provider (like * 'google.com') and signInFlow must be set to 'redirect'. * @return {boolean} Whether to skip the 'nascar' screen or not. */ federatedProviderShouldImmediatelyRedirect() { const immediateFederatedRedirect = !!this.config_.get( 'immediateFederatedRedirect'); const providers = this.getProviders(); const signInFlow = this.getSignInFlow(); return immediateFederatedRedirect && providers.length == 1 && idp.isFederatedSignInMethod(providers[0]) && signInFlow == Config.SignInFlow.REDIRECT; } /** @return {!Config.SignInFlow} The current sign-in flow. */ getSignInFlow() { const signInFlow = this.config_.get('signInFlow'); // Make sure the select flow is a valid one. for (let key in Config.SignInFlow) { if (Config.SignInFlow[key] == signInFlow) { // Return valid flow. return Config.SignInFlow[key]; } } // Default to redirect flow. return Config.SignInFlow.REDIRECT; } /** * @return {?function()} The callback to invoke when the widget UI is shown. */ getUiShownCallback() { return /** @type {?function()} */ ( this.getCallbacks_()['uiShown'] || null); } /** * @return {?function(?string, ?string)} The callback to invoke when the * widget UI is changed. Two parameters are passed, the from page * identifier and the to page identifier. */ getUiChangedCallback() { return /** @type {?function(?string, ?string)} */ ( this.getCallbacks_()['uiChanged'] || null); } /** * @return {?Config.signInSuccessCallback} The callback to invoke when the * user signs in successfully. The signed in firebase user is passed * into the callback. A second parameter, the Auth credential is also * returned if available from the sign in with redirect response. An * optional third parameter, the redirect URL, is also returned if that * value is set in storage. If it returns `true`, the widget will * continue to redirect the page to `signInSuccessUrl`. Otherwise, the * widget stops after it returns. */ getSignInSuccessCallback() { return /** @type {?Config.signInSuccessCallback} */ ( this.getCallbacks_()['signInSuccess'] || null); } /** * @return {?Config.signInSuccessWithAuthResultCallback} The callback to * invoke when the user signs in successfully. The Auth result is passed * into the callback, which includes current user, credential to sign in * to external Auth instance, additional user info and operation type. * An optional second parameter, the redirect URL, is also returned if * that value is set in storage. If it returns `true`, the widget will * continue to redirect the page to `signInSuccessUrl`. Otherwise, the * widget stops after it returns. */ getSignInSuccessWithAuthResultCallback() { return ( /** @type {?Config.signInSuccessWithAuthResultCallback} */ ( this.getCallbacks_()['signInSuccessWithAuthResult'] || null)); } /** * @return {?Config.signInFailureCallback} The callback to invoke when the * user fails to sign in. */ getSignInFailureCallback() { return /** @type {?Config.signInFailureCallback} */ ( this.getCallbacks_()['signInFailure'] || null); } /** * @return {!Object} The callback configuration. * @private */ getCallbacks_() { return /** @type {!Object} */ (this.config_.get('callbacks') || {}); } /** * @return {!Config.CredentialHelper} The credential helper to use. */ getCredentialHelper() { // Always use none for non http or https environment. // This could change when we support other credential helpers. This is // unlikely though as smartlock also checks the domain and will not work in // such environments. if (!util.isHttpOrHttps()) { return Config.CredentialHelper.NONE; } const credentialHelper = this.config_.get('credentialHelper'); // Make sure the credential helper is valid. for (let key in Config.CredentialHelper) { if (Config.CredentialHelper[key] === credentialHelper) { // Return valid flow. return Config.CredentialHelper[key]; } } // Default to using none. return Config.CredentialHelper.NONE; } /** * Resolves configurations that are implied/restricted by other configs. * @private */ resolveImplicitConfig_() { if (util.isMobileBrowser()) { // On mobile we should not use popup this.config_.update('popupMode', false); } } /** * Sets the configurations. * @param {Object} config The configurations. */ setConfig(config) { for (let name in config) { try { this.config_.update(name, config[name]); } catch (e) { log.error(`Invalid config: "${name}"`); } } this.resolveImplicitConfig_(); this.getPhoneAuthAvailableCountries(); } /** * Updates the configuration and its descendants with the given value. * @param {string} name The name of the configuration. * @param {*} value The value of the configuration. */ update(name, value) { this.config_.update(name, value); this.getPhoneAuthAvailableCountries(); } } /** * The different credentials helper available. * @enum {string} */ Config.CredentialHelper = { GOOGLE_YOLO: 'googleyolo', NONE: 'none', }; /** * Provider ID for continue as guest sign in option. * @const {string} */ Config.ANONYMOUS_PROVIDER_ID = 'anonymous'; /** * @typedef {{ * user: (?firebase.User), * credential: (?firebase.auth.AuthCredential), * operationType: (?string|undefined), * additionalUserInfo: (?firebase.auth.AdditionalUserInfo|undefined) * }} */ Config.AuthResult; /** * The configuration sign-in success callback. * @typedef {function( * !firebase.User, ?firebase.auth.AuthCredential=, string=): boolean} */ Config.signInSuccessCallback; /** * The configuration sign-in success callback which takes AuthResult as input. * @typedef {function(!Config.AuthResult, string=): boolean} */ Config.signInSuccessWithAuthResultCallback; /** * The configuration sign-in failure callback. * @typedef {function(!AuthUIError): (!Promise|void)} */ Config.signInFailureCallback; /** * The type of sign-in flow. * @enum {string} */ Config.SignInFlow = { POPUP: 'popup', REDIRECT: 'redirect', }; /** * The provider config object for generic providers. * providerId: The provider ID. * providerName: The display name of the provider. * fullLabel: The full button label. If both providerName and fullLabel are * provided, we will use fullLabel for long name and providerName for short * name. * buttonColor: The color of the sign in button. * iconUrl: The URL of the icon on sign in button. * loginHintKey: The name to use for the optional login hint parameter. * @typedef {{ * providerId: string, * fullLabel: (?string|undefined), * providerName: (?string|undefined), * buttonColor: (?string|undefined), * iconUrl: (?string|undefined), * loginHintKey: (?string|undefined) * }} */ Config.ProviderConfig; /** * Enums for callback widget mode. Please alphabetize by names. * @enum {string} */ Config.WidgetMode = { CALLBACK: 'callback', RECOVER_EMAIL: 'recoverEmail', RESET_PASSWORD: 'resetPassword', REVERT_SECOND_FACTOR_ADDITION: 'revertSecondFactorAddition', SELECT: 'select', SIGN_IN: 'signIn', VERIFY_AND_CHANGE_EMAIL: 'verifyAndChangeEmail', VERIFY_EMAIL: 'verifyEmail', }; /** * FirebaseUI supported providers in sign in option. * @const {!Array} */ const UI_SUPPORTED_PROVIDERS = ['anonymous']; /** * @const @type {!Array} List of blacklisted reCAPTCHA parameter * keys. */ const BLACKLISTED_RECAPTCHA_KEYS = [ 'sitekey', 'tabindex', 'callback', 'expired-callback']; exports = Config;