internal.js 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471
  1. import { as as _getInstance, at as _assert, au as _signInWithCredential, av as _reauthenticate, aw as _link$1, J as AuthCredential, ax as signInWithIdp, ay as _fail, az as debugAssert, aA as _persistenceKeyName, aB as _castAuth, aC as FederatedAuthProvider, aD as BaseOAuthProvider, aE as _emulatorUrl, aF as _performApiRequest, aG as _isIOS, aH as _isAndroid, aI as _isIOS7Or8, aJ as _createError, aK as _isIframe, aL as _isMobileBrowser, aM as _isIE10, aN as _isSafari } from './totp-e47c784e.js';
  2. export { A as ActionCodeOperation, ag as ActionCodeURL, J as AuthCredential, G as AuthErrorCodes, aP as AuthImpl, K as EmailAuthCredential, Q as EmailAuthProvider, U as FacebookAuthProvider, F as FactorId, aR as FetchProvider, W as GithubAuthProvider, V as GoogleAuthProvider, L as OAuthCredential, X as OAuthProvider, O as OperationType, M as PhoneAuthCredential, P as PhoneAuthProvider, m as PhoneMultiFactorGenerator, p as ProviderId, R as RecaptchaVerifier, aS as SAMLAuthCredential, Y as SAMLAuthProvider, S as SignInMethod, T as TotpMultiFactorGenerator, n as TotpSecret, Z as TwitterAuthProvider, aO as UserImpl, at as _assert, aB as _castAuth, ay as _fail, aQ as _getClientVersion, as as _getInstance, aA as _persistenceKeyName, a5 as applyActionCode, w as beforeAuthStateChanged, b as browserLocalPersistence, k as browserPopupRedirectResolver, a as browserSessionPersistence, a6 as checkActionCode, a4 as confirmPasswordReset, I as connectAuthEmulator, a8 as createUserWithEmailAndPassword, D as debugErrorMap, C as deleteUser, ad as fetchSignInMethodsForEmail, ao as getAdditionalUserInfo, o as getAuth, al as getIdToken, am as getIdTokenResult, aq as getMultiFactorResolver, j as getRedirectResult, N as inMemoryPersistence, i as indexedDBLocalPersistence, H as initializeAuth, t as initializeRecaptchaConfig, ab as isSignInWithEmailLink, a0 as linkWithCredential, l as linkWithPhoneNumber, d as linkWithPopup, g as linkWithRedirect, ar as multiFactor, x as onAuthStateChanged, v as onIdTokenChanged, ah as parseActionCodeURL, E as prodErrorMap, a1 as reauthenticateWithCredential, r as reauthenticateWithPhoneNumber, e as reauthenticateWithPopup, h as reauthenticateWithRedirect, ap as reload, ae as sendEmailVerification, a3 as sendPasswordResetEmail, aa as sendSignInLinkToEmail, q as setPersistence, _ as signInAnonymously, $ as signInWithCredential, a2 as signInWithCustomToken, a9 as signInWithEmailAndPassword, ac as signInWithEmailLink, s as signInWithPhoneNumber, c as signInWithPopup, f as signInWithRedirect, B as signOut, an as unlink, z as updateCurrentUser, aj as updateEmail, ak as updatePassword, u as updatePhoneNumber, ai as updateProfile, y as useDeviceLanguage, af as verifyBeforeUpdateEmail, a7 as verifyPasswordResetCode } from './totp-e47c784e.js';
  3. import { isEmpty, querystring, getUA, querystringDecode } from '@firebase/util';
  4. import 'tslib';
  5. import { SDK_VERSION } from '@firebase/app';
  6. import '@firebase/component';
  7. import 'node-fetch';
  8. import '@firebase/logger';
  9. /**
  10. * @license
  11. * Copyright 2020 Google LLC
  12. *
  13. * Licensed under the Apache License, Version 2.0 (the "License");
  14. * you may not use this file except in compliance with the License.
  15. * You may obtain a copy of the License at
  16. *
  17. * http://www.apache.org/licenses/LICENSE-2.0
  18. *
  19. * Unless required by applicable law or agreed to in writing, software
  20. * distributed under the License is distributed on an "AS IS" BASIS,
  21. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22. * See the License for the specific language governing permissions and
  23. * limitations under the License.
  24. */
  25. function _generateEventId(prefix = '', digits = 10) {
  26. let random = '';
  27. for (let i = 0; i < digits; i++) {
  28. random += Math.floor(Math.random() * 10);
  29. }
  30. return prefix + random;
  31. }
  32. /**
  33. * @license
  34. * Copyright 2020 Google LLC.
  35. *
  36. * Licensed under the Apache License, Version 2.0 (the "License");
  37. * you may not use this file except in compliance with the License.
  38. * You may obtain a copy of the License at
  39. *
  40. * http://www.apache.org/licenses/LICENSE-2.0
  41. *
  42. * Unless required by applicable law or agreed to in writing, software
  43. * distributed under the License is distributed on an "AS IS" BASIS,
  44. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  45. * See the License for the specific language governing permissions and
  46. * limitations under the License.
  47. */
  48. class AuthPopup {
  49. constructor(window) {
  50. this.window = window;
  51. this.associatedEvent = null;
  52. }
  53. close() {
  54. if (this.window) {
  55. try {
  56. this.window.close();
  57. }
  58. catch (e) { }
  59. }
  60. }
  61. }
  62. /**
  63. * @license
  64. * Copyright 2021 Google LLC
  65. *
  66. * Licensed under the Apache License, Version 2.0 (the "License");
  67. * you may not use this file except in compliance with the License.
  68. * You may obtain a copy of the License at
  69. *
  70. * http://www.apache.org/licenses/LICENSE-2.0
  71. *
  72. * Unless required by applicable law or agreed to in writing, software
  73. * distributed under the License is distributed on an "AS IS" BASIS,
  74. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  75. * See the License for the specific language governing permissions and
  76. * limitations under the License.
  77. */
  78. /**
  79. * Chooses a popup/redirect resolver to use. This prefers the override (which
  80. * is directly passed in), and falls back to the property set on the auth
  81. * object. If neither are available, this function errors w/ an argument error.
  82. */
  83. function _withDefaultResolver(auth, resolverOverride) {
  84. if (resolverOverride) {
  85. return _getInstance(resolverOverride);
  86. }
  87. _assert(auth._popupRedirectResolver, auth, "argument-error" /* AuthErrorCode.ARGUMENT_ERROR */);
  88. return auth._popupRedirectResolver;
  89. }
  90. /**
  91. * @license
  92. * Copyright 2019 Google LLC
  93. *
  94. * Licensed under the Apache License, Version 2.0 (the "License");
  95. * you may not use this file except in compliance with the License.
  96. * You may obtain a copy of the License at
  97. *
  98. * http://www.apache.org/licenses/LICENSE-2.0
  99. *
  100. * Unless required by applicable law or agreed to in writing, software
  101. * distributed under the License is distributed on an "AS IS" BASIS,
  102. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  103. * See the License for the specific language governing permissions and
  104. * limitations under the License.
  105. */
  106. class IdpCredential extends AuthCredential {
  107. constructor(params) {
  108. super("custom" /* ProviderId.CUSTOM */, "custom" /* ProviderId.CUSTOM */);
  109. this.params = params;
  110. }
  111. _getIdTokenResponse(auth) {
  112. return signInWithIdp(auth, this._buildIdpRequest());
  113. }
  114. _linkToIdToken(auth, idToken) {
  115. return signInWithIdp(auth, this._buildIdpRequest(idToken));
  116. }
  117. _getReauthenticationResolver(auth) {
  118. return signInWithIdp(auth, this._buildIdpRequest());
  119. }
  120. _buildIdpRequest(idToken) {
  121. const request = {
  122. requestUri: this.params.requestUri,
  123. sessionId: this.params.sessionId,
  124. postBody: this.params.postBody,
  125. tenantId: this.params.tenantId,
  126. pendingToken: this.params.pendingToken,
  127. returnSecureToken: true,
  128. returnIdpCredential: true
  129. };
  130. if (idToken) {
  131. request.idToken = idToken;
  132. }
  133. return request;
  134. }
  135. }
  136. function _signIn(params) {
  137. return _signInWithCredential(params.auth, new IdpCredential(params), params.bypassAuthState);
  138. }
  139. function _reauth(params) {
  140. const { auth, user } = params;
  141. _assert(user, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
  142. return _reauthenticate(user, new IdpCredential(params), params.bypassAuthState);
  143. }
  144. async function _link(params) {
  145. const { auth, user } = params;
  146. _assert(user, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
  147. return _link$1(user, new IdpCredential(params), params.bypassAuthState);
  148. }
  149. /**
  150. * @license
  151. * Copyright 2020 Google LLC
  152. *
  153. * Licensed under the Apache License, Version 2.0 (the "License");
  154. * you may not use this file except in compliance with the License.
  155. * You may obtain a copy of the License at
  156. *
  157. * http://www.apache.org/licenses/LICENSE-2.0
  158. *
  159. * Unless required by applicable law or agreed to in writing, software
  160. * distributed under the License is distributed on an "AS IS" BASIS,
  161. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  162. * See the License for the specific language governing permissions and
  163. * limitations under the License.
  164. */
  165. /**
  166. * Popup event manager. Handles the popup's entire lifecycle; listens to auth
  167. * events
  168. */
  169. class AbstractPopupRedirectOperation {
  170. constructor(auth, filter, resolver, user, bypassAuthState = false) {
  171. this.auth = auth;
  172. this.resolver = resolver;
  173. this.user = user;
  174. this.bypassAuthState = bypassAuthState;
  175. this.pendingPromise = null;
  176. this.eventManager = null;
  177. this.filter = Array.isArray(filter) ? filter : [filter];
  178. }
  179. execute() {
  180. return new Promise(async (resolve, reject) => {
  181. this.pendingPromise = { resolve, reject };
  182. try {
  183. this.eventManager = await this.resolver._initialize(this.auth);
  184. await this.onExecution();
  185. this.eventManager.registerConsumer(this);
  186. }
  187. catch (e) {
  188. this.reject(e);
  189. }
  190. });
  191. }
  192. async onAuthEvent(event) {
  193. const { urlResponse, sessionId, postBody, tenantId, error, type } = event;
  194. if (error) {
  195. this.reject(error);
  196. return;
  197. }
  198. const params = {
  199. auth: this.auth,
  200. requestUri: urlResponse,
  201. sessionId: sessionId,
  202. tenantId: tenantId || undefined,
  203. postBody: postBody || undefined,
  204. user: this.user,
  205. bypassAuthState: this.bypassAuthState
  206. };
  207. try {
  208. this.resolve(await this.getIdpTask(type)(params));
  209. }
  210. catch (e) {
  211. this.reject(e);
  212. }
  213. }
  214. onError(error) {
  215. this.reject(error);
  216. }
  217. getIdpTask(type) {
  218. switch (type) {
  219. case "signInViaPopup" /* AuthEventType.SIGN_IN_VIA_POPUP */:
  220. case "signInViaRedirect" /* AuthEventType.SIGN_IN_VIA_REDIRECT */:
  221. return _signIn;
  222. case "linkViaPopup" /* AuthEventType.LINK_VIA_POPUP */:
  223. case "linkViaRedirect" /* AuthEventType.LINK_VIA_REDIRECT */:
  224. return _link;
  225. case "reauthViaPopup" /* AuthEventType.REAUTH_VIA_POPUP */:
  226. case "reauthViaRedirect" /* AuthEventType.REAUTH_VIA_REDIRECT */:
  227. return _reauth;
  228. default:
  229. _fail(this.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
  230. }
  231. }
  232. resolve(cred) {
  233. debugAssert(this.pendingPromise, 'Pending promise was never set');
  234. this.pendingPromise.resolve(cred);
  235. this.unregisterAndCleanUp();
  236. }
  237. reject(error) {
  238. debugAssert(this.pendingPromise, 'Pending promise was never set');
  239. this.pendingPromise.reject(error);
  240. this.unregisterAndCleanUp();
  241. }
  242. unregisterAndCleanUp() {
  243. if (this.eventManager) {
  244. this.eventManager.unregisterConsumer(this);
  245. }
  246. this.pendingPromise = null;
  247. this.cleanUp();
  248. }
  249. }
  250. /**
  251. * @license
  252. * Copyright 2020 Google LLC
  253. *
  254. * Licensed under the Apache License, Version 2.0 (the "License");
  255. * you may not use this file except in compliance with the License.
  256. * You may obtain a copy of the License at
  257. *
  258. * http://www.apache.org/licenses/LICENSE-2.0
  259. *
  260. * Unless required by applicable law or agreed to in writing, software
  261. * distributed under the License is distributed on an "AS IS" BASIS,
  262. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  263. * See the License for the specific language governing permissions and
  264. * limitations under the License.
  265. */
  266. const PENDING_REDIRECT_KEY = 'pendingRedirect';
  267. // We only get one redirect outcome for any one auth, so just store it
  268. // in here.
  269. const redirectOutcomeMap = new Map();
  270. class RedirectAction extends AbstractPopupRedirectOperation {
  271. constructor(auth, resolver, bypassAuthState = false) {
  272. super(auth, [
  273. "signInViaRedirect" /* AuthEventType.SIGN_IN_VIA_REDIRECT */,
  274. "linkViaRedirect" /* AuthEventType.LINK_VIA_REDIRECT */,
  275. "reauthViaRedirect" /* AuthEventType.REAUTH_VIA_REDIRECT */,
  276. "unknown" /* AuthEventType.UNKNOWN */
  277. ], resolver, undefined, bypassAuthState);
  278. this.eventId = null;
  279. }
  280. /**
  281. * Override the execute function; if we already have a redirect result, then
  282. * just return it.
  283. */
  284. async execute() {
  285. let readyOutcome = redirectOutcomeMap.get(this.auth._key());
  286. if (!readyOutcome) {
  287. try {
  288. const hasPendingRedirect = await _getAndClearPendingRedirectStatus(this.resolver, this.auth);
  289. const result = hasPendingRedirect ? await super.execute() : null;
  290. readyOutcome = () => Promise.resolve(result);
  291. }
  292. catch (e) {
  293. readyOutcome = () => Promise.reject(e);
  294. }
  295. redirectOutcomeMap.set(this.auth._key(), readyOutcome);
  296. }
  297. // If we're not bypassing auth state, the ready outcome should be set to
  298. // null.
  299. if (!this.bypassAuthState) {
  300. redirectOutcomeMap.set(this.auth._key(), () => Promise.resolve(null));
  301. }
  302. return readyOutcome();
  303. }
  304. async onAuthEvent(event) {
  305. if (event.type === "signInViaRedirect" /* AuthEventType.SIGN_IN_VIA_REDIRECT */) {
  306. return super.onAuthEvent(event);
  307. }
  308. else if (event.type === "unknown" /* AuthEventType.UNKNOWN */) {
  309. // This is a sentinel value indicating there's no pending redirect
  310. this.resolve(null);
  311. return;
  312. }
  313. if (event.eventId) {
  314. const user = await this.auth._redirectUserForId(event.eventId);
  315. if (user) {
  316. this.user = user;
  317. return super.onAuthEvent(event);
  318. }
  319. else {
  320. this.resolve(null);
  321. }
  322. }
  323. }
  324. async onExecution() { }
  325. cleanUp() { }
  326. }
  327. async function _getAndClearPendingRedirectStatus(resolver, auth) {
  328. const key = pendingRedirectKey(auth);
  329. const persistence = resolverPersistence(resolver);
  330. if (!(await persistence._isAvailable())) {
  331. return false;
  332. }
  333. const hasPendingRedirect = (await persistence._get(key)) === 'true';
  334. await persistence._remove(key);
  335. return hasPendingRedirect;
  336. }
  337. function _clearRedirectOutcomes() {
  338. redirectOutcomeMap.clear();
  339. }
  340. function _overrideRedirectResult(auth, result) {
  341. redirectOutcomeMap.set(auth._key(), result);
  342. }
  343. function resolverPersistence(resolver) {
  344. return _getInstance(resolver._redirectPersistence);
  345. }
  346. function pendingRedirectKey(auth) {
  347. return _persistenceKeyName(PENDING_REDIRECT_KEY, auth.config.apiKey, auth.name);
  348. }
  349. /**
  350. * @license
  351. * Copyright 2020 Google LLC
  352. *
  353. * Licensed under the Apache License, Version 2.0 (the "License");
  354. * you may not use this file except in compliance with the License.
  355. * You may obtain a copy of the License at
  356. *
  357. * http://www.apache.org/licenses/LICENSE-2.0
  358. *
  359. * Unless required by applicable law or agreed to in writing, software
  360. * distributed under the License is distributed on an "AS IS" BASIS,
  361. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  362. * See the License for the specific language governing permissions and
  363. * limitations under the License.
  364. */
  365. async function _getRedirectResult(auth, resolverExtern, bypassAuthState = false) {
  366. const authInternal = _castAuth(auth);
  367. const resolver = _withDefaultResolver(authInternal, resolverExtern);
  368. const action = new RedirectAction(authInternal, resolver, bypassAuthState);
  369. const result = await action.execute();
  370. if (result && !bypassAuthState) {
  371. delete result.user._redirectEventId;
  372. await authInternal._persistUserIfCurrent(result.user);
  373. await authInternal._setRedirectUser(null, resolverExtern);
  374. }
  375. return result;
  376. }
  377. const STORAGE_AVAILABLE_KEY = '__sak';
  378. /**
  379. * @license
  380. * Copyright 2019 Google LLC
  381. *
  382. * Licensed under the Apache License, Version 2.0 (the "License");
  383. * you may not use this file except in compliance with the License.
  384. * You may obtain a copy of the License at
  385. *
  386. * http://www.apache.org/licenses/LICENSE-2.0
  387. *
  388. * Unless required by applicable law or agreed to in writing, software
  389. * distributed under the License is distributed on an "AS IS" BASIS,
  390. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  391. * See the License for the specific language governing permissions and
  392. * limitations under the License.
  393. */
  394. // There are two different browser persistence types: local and session.
  395. // Both have the same implementation but use a different underlying storage
  396. // object.
  397. class BrowserPersistenceClass {
  398. constructor(storageRetriever, type) {
  399. this.storageRetriever = storageRetriever;
  400. this.type = type;
  401. }
  402. _isAvailable() {
  403. try {
  404. if (!this.storage) {
  405. return Promise.resolve(false);
  406. }
  407. this.storage.setItem(STORAGE_AVAILABLE_KEY, '1');
  408. this.storage.removeItem(STORAGE_AVAILABLE_KEY);
  409. return Promise.resolve(true);
  410. }
  411. catch (_a) {
  412. return Promise.resolve(false);
  413. }
  414. }
  415. _set(key, value) {
  416. this.storage.setItem(key, JSON.stringify(value));
  417. return Promise.resolve();
  418. }
  419. _get(key) {
  420. const json = this.storage.getItem(key);
  421. return Promise.resolve(json ? JSON.parse(json) : null);
  422. }
  423. _remove(key) {
  424. this.storage.removeItem(key);
  425. return Promise.resolve();
  426. }
  427. get storage() {
  428. return this.storageRetriever();
  429. }
  430. }
  431. /**
  432. * @license
  433. * Copyright 2020 Google LLC
  434. *
  435. * Licensed under the Apache License, Version 2.0 (the "License");
  436. * you may not use this file except in compliance with the License.
  437. * You may obtain a copy of the License at
  438. *
  439. * http://www.apache.org/licenses/LICENSE-2.0
  440. *
  441. * Unless required by applicable law or agreed to in writing, software
  442. * distributed under the License is distributed on an "AS IS" BASIS,
  443. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  444. * See the License for the specific language governing permissions and
  445. * limitations under the License.
  446. */
  447. class BrowserSessionPersistence extends BrowserPersistenceClass {
  448. constructor() {
  449. super(() => window.sessionStorage, "SESSION" /* PersistenceType.SESSION */);
  450. }
  451. _addListener(_key, _listener) {
  452. // Listeners are not supported for session storage since it cannot be shared across windows
  453. return;
  454. }
  455. _removeListener(_key, _listener) {
  456. // Listeners are not supported for session storage since it cannot be shared across windows
  457. return;
  458. }
  459. }
  460. BrowserSessionPersistence.type = 'SESSION';
  461. /**
  462. * An implementation of {@link Persistence} of `SESSION` using `sessionStorage`
  463. * for the underlying storage.
  464. *
  465. * @public
  466. */
  467. const browserSessionPersistence = BrowserSessionPersistence;
  468. /**
  469. * @license
  470. * Copyright 2021 Google LLC
  471. *
  472. * Licensed under the Apache License, Version 2.0 (the "License");
  473. * you may not use this file except in compliance with the License.
  474. * You may obtain a copy of the License at
  475. *
  476. * http://www.apache.org/licenses/LICENSE-2.0
  477. *
  478. * Unless required by applicable law or agreed to in writing, software
  479. * distributed under the License is distributed on an "AS IS" BASIS,
  480. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  481. * See the License for the specific language governing permissions and
  482. * limitations under the License.
  483. */
  484. /**
  485. * URL for Authentication widget which will initiate the OAuth handshake
  486. *
  487. * @internal
  488. */
  489. const WIDGET_PATH = '__/auth/handler';
  490. /**
  491. * URL for emulated environment
  492. *
  493. * @internal
  494. */
  495. const EMULATOR_WIDGET_PATH = 'emulator/auth/handler';
  496. /**
  497. * Fragment name for the App Check token that gets passed to the widget
  498. *
  499. * @internal
  500. */
  501. const FIREBASE_APP_CHECK_FRAGMENT_ID = encodeURIComponent('fac');
  502. async function _getRedirectUrl(auth, provider, authType, redirectUrl, eventId, additionalParams) {
  503. _assert(auth.config.authDomain, auth, "auth-domain-config-required" /* AuthErrorCode.MISSING_AUTH_DOMAIN */);
  504. _assert(auth.config.apiKey, auth, "invalid-api-key" /* AuthErrorCode.INVALID_API_KEY */);
  505. const params = {
  506. apiKey: auth.config.apiKey,
  507. appName: auth.name,
  508. authType,
  509. redirectUrl,
  510. v: SDK_VERSION,
  511. eventId
  512. };
  513. if (provider instanceof FederatedAuthProvider) {
  514. provider.setDefaultLanguage(auth.languageCode);
  515. params.providerId = provider.providerId || '';
  516. if (!isEmpty(provider.getCustomParameters())) {
  517. params.customParameters = JSON.stringify(provider.getCustomParameters());
  518. }
  519. // TODO set additionalParams from the provider as well?
  520. for (const [key, value] of Object.entries(additionalParams || {})) {
  521. params[key] = value;
  522. }
  523. }
  524. if (provider instanceof BaseOAuthProvider) {
  525. const scopes = provider.getScopes().filter(scope => scope !== '');
  526. if (scopes.length > 0) {
  527. params.scopes = scopes.join(',');
  528. }
  529. }
  530. if (auth.tenantId) {
  531. params.tid = auth.tenantId;
  532. }
  533. // TODO: maybe set eid as endipointId
  534. // TODO: maybe set fw as Frameworks.join(",")
  535. const paramsDict = params;
  536. for (const key of Object.keys(paramsDict)) {
  537. if (paramsDict[key] === undefined) {
  538. delete paramsDict[key];
  539. }
  540. }
  541. // Sets the App Check token to pass to the widget
  542. const appCheckToken = await auth._getAppCheckToken();
  543. const appCheckTokenFragment = appCheckToken
  544. ? `#${FIREBASE_APP_CHECK_FRAGMENT_ID}=${encodeURIComponent(appCheckToken)}`
  545. : '';
  546. // Start at index 1 to skip the leading '&' in the query string
  547. return `${getHandlerBase(auth)}?${querystring(paramsDict).slice(1)}${appCheckTokenFragment}`;
  548. }
  549. function getHandlerBase({ config }) {
  550. if (!config.emulator) {
  551. return `https://${config.authDomain}/${WIDGET_PATH}`;
  552. }
  553. return _emulatorUrl(config, EMULATOR_WIDGET_PATH);
  554. }
  555. /**
  556. * @license
  557. * Copyright 2021 Google LLC
  558. *
  559. * Licensed under the Apache License, Version 2.0 (the "License");
  560. * you may not use this file except in compliance with the License.
  561. * You may obtain a copy of the License at
  562. *
  563. * http://www.apache.org/licenses/LICENSE-2.0
  564. *
  565. * Unless required by applicable law or agreed to in writing, software
  566. * distributed under the License is distributed on an "AS IS" BASIS,
  567. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  568. * See the License for the specific language governing permissions and
  569. * limitations under the License.
  570. */
  571. function _cordovaWindow() {
  572. return window;
  573. }
  574. /**
  575. * @license
  576. * Copyright 2020 Google LLC
  577. *
  578. * Licensed under the Apache License, Version 2.0 (the "License");
  579. * you may not use this file except in compliance with the License.
  580. * You may obtain a copy of the License at
  581. *
  582. * http://www.apache.org/licenses/LICENSE-2.0
  583. *
  584. * Unless required by applicable law or agreed to in writing, software
  585. * distributed under the License is distributed on an "AS IS" BASIS,
  586. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  587. * See the License for the specific language governing permissions and
  588. * limitations under the License.
  589. */
  590. async function _getProjectConfig(auth, request = {}) {
  591. return _performApiRequest(auth, "GET" /* HttpMethod.GET */, "/v1/projects" /* Endpoint.GET_PROJECT_CONFIG */, request);
  592. }
  593. /**
  594. * @license
  595. * Copyright 2020 Google LLC
  596. *
  597. * Licensed under the Apache License, Version 2.0 (the "License");
  598. * you may not use this file except in compliance with the License.
  599. * You may obtain a copy of the License at
  600. *
  601. * http://www.apache.org/licenses/LICENSE-2.0
  602. *
  603. * Unless required by applicable law or agreed to in writing, software
  604. * distributed under the License is distributed on an "AS IS" BASIS,
  605. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  606. * See the License for the specific language governing permissions and
  607. * limitations under the License.
  608. */
  609. /**
  610. * How long to wait after the app comes back into focus before concluding that
  611. * the user closed the sign in tab.
  612. */
  613. const REDIRECT_TIMEOUT_MS = 2000;
  614. /**
  615. * Generates the URL for the OAuth handler.
  616. */
  617. async function _generateHandlerUrl(auth, event, provider) {
  618. var _a;
  619. // Get the cordova plugins
  620. const { BuildInfo } = _cordovaWindow();
  621. debugAssert(event.sessionId, 'AuthEvent did not contain a session ID');
  622. const sessionDigest = await computeSha256(event.sessionId);
  623. const additionalParams = {};
  624. if (_isIOS()) {
  625. // iOS app identifier
  626. additionalParams['ibi'] = BuildInfo.packageName;
  627. }
  628. else if (_isAndroid()) {
  629. // Android app identifier
  630. additionalParams['apn'] = BuildInfo.packageName;
  631. }
  632. else {
  633. _fail(auth, "operation-not-supported-in-this-environment" /* AuthErrorCode.OPERATION_NOT_SUPPORTED */);
  634. }
  635. // Add the display name if available
  636. if (BuildInfo.displayName) {
  637. additionalParams['appDisplayName'] = BuildInfo.displayName;
  638. }
  639. // Attached the hashed session ID
  640. additionalParams['sessionId'] = sessionDigest;
  641. return _getRedirectUrl(auth, provider, event.type, undefined, (_a = event.eventId) !== null && _a !== void 0 ? _a : undefined, additionalParams);
  642. }
  643. /**
  644. * Validates that this app is valid for this project configuration
  645. */
  646. async function _validateOrigin(auth) {
  647. const { BuildInfo } = _cordovaWindow();
  648. const request = {};
  649. if (_isIOS()) {
  650. request.iosBundleId = BuildInfo.packageName;
  651. }
  652. else if (_isAndroid()) {
  653. request.androidPackageName = BuildInfo.packageName;
  654. }
  655. else {
  656. _fail(auth, "operation-not-supported-in-this-environment" /* AuthErrorCode.OPERATION_NOT_SUPPORTED */);
  657. }
  658. // Will fail automatically if package name is not authorized
  659. await _getProjectConfig(auth, request);
  660. }
  661. function _performRedirect(handlerUrl) {
  662. // Get the cordova plugins
  663. const { cordova } = _cordovaWindow();
  664. return new Promise(resolve => {
  665. cordova.plugins.browsertab.isAvailable(browserTabIsAvailable => {
  666. let iabRef = null;
  667. if (browserTabIsAvailable) {
  668. cordova.plugins.browsertab.openUrl(handlerUrl);
  669. }
  670. else {
  671. // TODO: Return the inappbrowser ref that's returned from the open call
  672. iabRef = cordova.InAppBrowser.open(handlerUrl, _isIOS7Or8() ? '_blank' : '_system', 'location=yes');
  673. }
  674. resolve(iabRef);
  675. });
  676. });
  677. }
  678. /**
  679. * This function waits for app activity to be seen before resolving. It does
  680. * this by attaching listeners to various dom events. Once the app is determined
  681. * to be visible, this promise resolves. AFTER that resolution, the listeners
  682. * are detached and any browser tabs left open will be closed.
  683. */
  684. async function _waitForAppResume(auth, eventListener, iabRef) {
  685. // Get the cordova plugins
  686. const { cordova } = _cordovaWindow();
  687. let cleanup = () => { };
  688. try {
  689. await new Promise((resolve, reject) => {
  690. let onCloseTimer = null;
  691. // DEFINE ALL THE CALLBACKS =====
  692. function authEventSeen() {
  693. var _a;
  694. // Auth event was detected. Resolve this promise and close the extra
  695. // window if it's still open.
  696. resolve();
  697. const closeBrowserTab = (_a = cordova.plugins.browsertab) === null || _a === void 0 ? void 0 : _a.close;
  698. if (typeof closeBrowserTab === 'function') {
  699. closeBrowserTab();
  700. }
  701. // Close inappbrowser emebedded webview in iOS7 and 8 case if still
  702. // open.
  703. if (typeof (iabRef === null || iabRef === void 0 ? void 0 : iabRef.close) === 'function') {
  704. iabRef.close();
  705. }
  706. }
  707. function resumed() {
  708. if (onCloseTimer) {
  709. // This code already ran; do not rerun.
  710. return;
  711. }
  712. onCloseTimer = window.setTimeout(() => {
  713. // Wait two seeconds after resume then reject.
  714. reject(_createError(auth, "redirect-cancelled-by-user" /* AuthErrorCode.REDIRECT_CANCELLED_BY_USER */));
  715. }, REDIRECT_TIMEOUT_MS);
  716. }
  717. function visibilityChanged() {
  718. if ((document === null || document === void 0 ? void 0 : document.visibilityState) === 'visible') {
  719. resumed();
  720. }
  721. }
  722. // ATTACH ALL THE LISTENERS =====
  723. // Listen for the auth event
  724. eventListener.addPassiveListener(authEventSeen);
  725. // Listen for resume and visibility events
  726. document.addEventListener('resume', resumed, false);
  727. if (_isAndroid()) {
  728. document.addEventListener('visibilitychange', visibilityChanged, false);
  729. }
  730. // SETUP THE CLEANUP FUNCTION =====
  731. cleanup = () => {
  732. eventListener.removePassiveListener(authEventSeen);
  733. document.removeEventListener('resume', resumed, false);
  734. document.removeEventListener('visibilitychange', visibilityChanged, false);
  735. if (onCloseTimer) {
  736. window.clearTimeout(onCloseTimer);
  737. }
  738. };
  739. });
  740. }
  741. finally {
  742. cleanup();
  743. }
  744. }
  745. /**
  746. * Checks the configuration of the Cordova environment. This has no side effect
  747. * if the configuration is correct; otherwise it throws an error with the
  748. * missing plugin.
  749. */
  750. function _checkCordovaConfiguration(auth) {
  751. var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
  752. const win = _cordovaWindow();
  753. // Check all dependencies installed.
  754. // https://github.com/nordnet/cordova-universal-links-plugin
  755. // Note that cordova-universal-links-plugin has been abandoned.
  756. // A fork with latest fixes is available at:
  757. // https://www.npmjs.com/package/cordova-universal-links-plugin-fix
  758. _assert(typeof ((_a = win === null || win === void 0 ? void 0 : win.universalLinks) === null || _a === void 0 ? void 0 : _a.subscribe) === 'function', auth, "invalid-cordova-configuration" /* AuthErrorCode.INVALID_CORDOVA_CONFIGURATION */, {
  759. missingPlugin: 'cordova-universal-links-plugin-fix'
  760. });
  761. // https://www.npmjs.com/package/cordova-plugin-buildinfo
  762. _assert(typeof ((_b = win === null || win === void 0 ? void 0 : win.BuildInfo) === null || _b === void 0 ? void 0 : _b.packageName) !== 'undefined', auth, "invalid-cordova-configuration" /* AuthErrorCode.INVALID_CORDOVA_CONFIGURATION */, {
  763. missingPlugin: 'cordova-plugin-buildInfo'
  764. });
  765. // https://github.com/google/cordova-plugin-browsertab
  766. _assert(typeof ((_e = (_d = (_c = win === null || win === void 0 ? void 0 : win.cordova) === null || _c === void 0 ? void 0 : _c.plugins) === null || _d === void 0 ? void 0 : _d.browsertab) === null || _e === void 0 ? void 0 : _e.openUrl) === 'function', auth, "invalid-cordova-configuration" /* AuthErrorCode.INVALID_CORDOVA_CONFIGURATION */, {
  767. missingPlugin: 'cordova-plugin-browsertab'
  768. });
  769. _assert(typeof ((_h = (_g = (_f = win === null || win === void 0 ? void 0 : win.cordova) === null || _f === void 0 ? void 0 : _f.plugins) === null || _g === void 0 ? void 0 : _g.browsertab) === null || _h === void 0 ? void 0 : _h.isAvailable) === 'function', auth, "invalid-cordova-configuration" /* AuthErrorCode.INVALID_CORDOVA_CONFIGURATION */, {
  770. missingPlugin: 'cordova-plugin-browsertab'
  771. });
  772. // https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/
  773. _assert(typeof ((_k = (_j = win === null || win === void 0 ? void 0 : win.cordova) === null || _j === void 0 ? void 0 : _j.InAppBrowser) === null || _k === void 0 ? void 0 : _k.open) === 'function', auth, "invalid-cordova-configuration" /* AuthErrorCode.INVALID_CORDOVA_CONFIGURATION */, {
  774. missingPlugin: 'cordova-plugin-inappbrowser'
  775. });
  776. }
  777. /**
  778. * Computes the SHA-256 of a session ID. The SubtleCrypto interface is only
  779. * available in "secure" contexts, which covers Cordova (which is served on a file
  780. * protocol).
  781. */
  782. async function computeSha256(sessionId) {
  783. const bytes = stringToArrayBuffer(sessionId);
  784. // TODO: For IE11 crypto has a different name and this operation comes back
  785. // as an object, not a promise. This is the old proposed standard that
  786. // is used by IE11:
  787. // https://www.w3.org/TR/2013/WD-WebCryptoAPI-20130108/#cryptooperation-interface
  788. const buf = await crypto.subtle.digest('SHA-256', bytes);
  789. const arr = Array.from(new Uint8Array(buf));
  790. return arr.map(num => num.toString(16).padStart(2, '0')).join('');
  791. }
  792. function stringToArrayBuffer(str) {
  793. // This function is only meant to deal with an ASCII charset and makes
  794. // certain simplifying assumptions.
  795. debugAssert(/[0-9a-zA-Z]+/.test(str), 'Can only convert alpha-numeric strings');
  796. if (typeof TextEncoder !== 'undefined') {
  797. return new TextEncoder().encode(str);
  798. }
  799. const buff = new ArrayBuffer(str.length);
  800. const view = new Uint8Array(buff);
  801. for (let i = 0; i < str.length; i++) {
  802. view[i] = str.charCodeAt(i);
  803. }
  804. return view;
  805. }
  806. /**
  807. * @license
  808. * Copyright 2020 Google LLC
  809. *
  810. * Licensed under the Apache License, Version 2.0 (the "License");
  811. * you may not use this file except in compliance with the License.
  812. * You may obtain a copy of the License at
  813. *
  814. * http://www.apache.org/licenses/LICENSE-2.0
  815. *
  816. * Unless required by applicable law or agreed to in writing, software
  817. * distributed under the License is distributed on an "AS IS" BASIS,
  818. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  819. * See the License for the specific language governing permissions and
  820. * limitations under the License.
  821. */
  822. // The amount of time to store the UIDs of seen events; this is
  823. // set to 10 min by default
  824. const EVENT_DUPLICATION_CACHE_DURATION_MS = 10 * 60 * 1000;
  825. class AuthEventManager {
  826. constructor(auth) {
  827. this.auth = auth;
  828. this.cachedEventUids = new Set();
  829. this.consumers = new Set();
  830. this.queuedRedirectEvent = null;
  831. this.hasHandledPotentialRedirect = false;
  832. this.lastProcessedEventTime = Date.now();
  833. }
  834. registerConsumer(authEventConsumer) {
  835. this.consumers.add(authEventConsumer);
  836. if (this.queuedRedirectEvent &&
  837. this.isEventForConsumer(this.queuedRedirectEvent, authEventConsumer)) {
  838. this.sendToConsumer(this.queuedRedirectEvent, authEventConsumer);
  839. this.saveEventToCache(this.queuedRedirectEvent);
  840. this.queuedRedirectEvent = null;
  841. }
  842. }
  843. unregisterConsumer(authEventConsumer) {
  844. this.consumers.delete(authEventConsumer);
  845. }
  846. onEvent(event) {
  847. // Check if the event has already been handled
  848. if (this.hasEventBeenHandled(event)) {
  849. return false;
  850. }
  851. let handled = false;
  852. this.consumers.forEach(consumer => {
  853. if (this.isEventForConsumer(event, consumer)) {
  854. handled = true;
  855. this.sendToConsumer(event, consumer);
  856. this.saveEventToCache(event);
  857. }
  858. });
  859. if (this.hasHandledPotentialRedirect || !isRedirectEvent(event)) {
  860. // If we've already seen a redirect before, or this is a popup event,
  861. // bail now
  862. return handled;
  863. }
  864. this.hasHandledPotentialRedirect = true;
  865. // If the redirect wasn't handled, hang on to it
  866. if (!handled) {
  867. this.queuedRedirectEvent = event;
  868. handled = true;
  869. }
  870. return handled;
  871. }
  872. sendToConsumer(event, consumer) {
  873. var _a;
  874. if (event.error && !isNullRedirectEvent(event)) {
  875. const code = ((_a = event.error.code) === null || _a === void 0 ? void 0 : _a.split('auth/')[1]) ||
  876. "internal-error" /* AuthErrorCode.INTERNAL_ERROR */;
  877. consumer.onError(_createError(this.auth, code));
  878. }
  879. else {
  880. consumer.onAuthEvent(event);
  881. }
  882. }
  883. isEventForConsumer(event, consumer) {
  884. const eventIdMatches = consumer.eventId === null ||
  885. (!!event.eventId && event.eventId === consumer.eventId);
  886. return consumer.filter.includes(event.type) && eventIdMatches;
  887. }
  888. hasEventBeenHandled(event) {
  889. if (Date.now() - this.lastProcessedEventTime >=
  890. EVENT_DUPLICATION_CACHE_DURATION_MS) {
  891. this.cachedEventUids.clear();
  892. }
  893. return this.cachedEventUids.has(eventUid(event));
  894. }
  895. saveEventToCache(event) {
  896. this.cachedEventUids.add(eventUid(event));
  897. this.lastProcessedEventTime = Date.now();
  898. }
  899. }
  900. function eventUid(e) {
  901. return [e.type, e.eventId, e.sessionId, e.tenantId].filter(v => v).join('-');
  902. }
  903. function isNullRedirectEvent({ type, error }) {
  904. return (type === "unknown" /* AuthEventType.UNKNOWN */ &&
  905. (error === null || error === void 0 ? void 0 : error.code) === `auth/${"no-auth-event" /* AuthErrorCode.NO_AUTH_EVENT */}`);
  906. }
  907. function isRedirectEvent(event) {
  908. switch (event.type) {
  909. case "signInViaRedirect" /* AuthEventType.SIGN_IN_VIA_REDIRECT */:
  910. case "linkViaRedirect" /* AuthEventType.LINK_VIA_REDIRECT */:
  911. case "reauthViaRedirect" /* AuthEventType.REAUTH_VIA_REDIRECT */:
  912. return true;
  913. case "unknown" /* AuthEventType.UNKNOWN */:
  914. return isNullRedirectEvent(event);
  915. default:
  916. return false;
  917. }
  918. }
  919. /**
  920. * @license
  921. * Copyright 2020 Google LLC
  922. *
  923. * Licensed under the Apache License, Version 2.0 (the "License");
  924. * you may not use this file except in compliance with the License.
  925. * You may obtain a copy of the License at
  926. *
  927. * http://www.apache.org/licenses/LICENSE-2.0
  928. *
  929. * Unless required by applicable law or agreed to in writing, software
  930. * distributed under the License is distributed on an "AS IS" BASIS,
  931. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  932. * See the License for the specific language governing permissions and
  933. * limitations under the License.
  934. */
  935. function _iframeCannotSyncWebStorage() {
  936. const ua = getUA();
  937. return _isSafari(ua) || _isIOS(ua);
  938. }
  939. // The polling period in case events are not supported
  940. const _POLLING_INTERVAL_MS = 1000;
  941. // The IE 10 localStorage cross tab synchronization delay in milliseconds
  942. const IE10_LOCAL_STORAGE_SYNC_DELAY = 10;
  943. class BrowserLocalPersistence extends BrowserPersistenceClass {
  944. constructor() {
  945. super(() => window.localStorage, "LOCAL" /* PersistenceType.LOCAL */);
  946. this.boundEventHandler = (event, poll) => this.onStorageEvent(event, poll);
  947. this.listeners = {};
  948. this.localCache = {};
  949. // setTimeout return value is platform specific
  950. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  951. this.pollTimer = null;
  952. // Safari or iOS browser and embedded in an iframe.
  953. this.safariLocalStorageNotSynced = _iframeCannotSyncWebStorage() && _isIframe();
  954. // Whether to use polling instead of depending on window events
  955. this.fallbackToPolling = _isMobileBrowser();
  956. this._shouldAllowMigration = true;
  957. }
  958. forAllChangedKeys(cb) {
  959. // Check all keys with listeners on them.
  960. for (const key of Object.keys(this.listeners)) {
  961. // Get value from localStorage.
  962. const newValue = this.storage.getItem(key);
  963. const oldValue = this.localCache[key];
  964. // If local map value does not match, trigger listener with storage event.
  965. // Differentiate this simulated event from the real storage event.
  966. if (newValue !== oldValue) {
  967. cb(key, oldValue, newValue);
  968. }
  969. }
  970. }
  971. onStorageEvent(event, poll = false) {
  972. // Key would be null in some situations, like when localStorage is cleared
  973. if (!event.key) {
  974. this.forAllChangedKeys((key, _oldValue, newValue) => {
  975. this.notifyListeners(key, newValue);
  976. });
  977. return;
  978. }
  979. const key = event.key;
  980. // Check the mechanism how this event was detected.
  981. // The first event will dictate the mechanism to be used.
  982. if (poll) {
  983. // Environment detects storage changes via polling.
  984. // Remove storage event listener to prevent possible event duplication.
  985. this.detachListener();
  986. }
  987. else {
  988. // Environment detects storage changes via storage event listener.
  989. // Remove polling listener to prevent possible event duplication.
  990. this.stopPolling();
  991. }
  992. // Safari embedded iframe. Storage event will trigger with the delta
  993. // changes but no changes will be applied to the iframe localStorage.
  994. if (this.safariLocalStorageNotSynced) {
  995. // Get current iframe page value.
  996. const storedValue = this.storage.getItem(key);
  997. // Value not synchronized, synchronize manually.
  998. if (event.newValue !== storedValue) {
  999. if (event.newValue !== null) {
  1000. // Value changed from current value.
  1001. this.storage.setItem(key, event.newValue);
  1002. }
  1003. else {
  1004. // Current value deleted.
  1005. this.storage.removeItem(key);
  1006. }
  1007. }
  1008. else if (this.localCache[key] === event.newValue && !poll) {
  1009. // Already detected and processed, do not trigger listeners again.
  1010. return;
  1011. }
  1012. }
  1013. const triggerListeners = () => {
  1014. // Keep local map up to date in case storage event is triggered before
  1015. // poll.
  1016. const storedValue = this.storage.getItem(key);
  1017. if (!poll && this.localCache[key] === storedValue) {
  1018. // Real storage event which has already been detected, do nothing.
  1019. // This seems to trigger in some IE browsers for some reason.
  1020. return;
  1021. }
  1022. this.notifyListeners(key, storedValue);
  1023. };
  1024. const storedValue = this.storage.getItem(key);
  1025. if (_isIE10() &&
  1026. storedValue !== event.newValue &&
  1027. event.newValue !== event.oldValue) {
  1028. // IE 10 has this weird bug where a storage event would trigger with the
  1029. // correct key, oldValue and newValue but localStorage.getItem(key) does
  1030. // not yield the updated value until a few milliseconds. This ensures
  1031. // this recovers from that situation.
  1032. setTimeout(triggerListeners, IE10_LOCAL_STORAGE_SYNC_DELAY);
  1033. }
  1034. else {
  1035. triggerListeners();
  1036. }
  1037. }
  1038. notifyListeners(key, value) {
  1039. this.localCache[key] = value;
  1040. const listeners = this.listeners[key];
  1041. if (listeners) {
  1042. for (const listener of Array.from(listeners)) {
  1043. listener(value ? JSON.parse(value) : value);
  1044. }
  1045. }
  1046. }
  1047. startPolling() {
  1048. this.stopPolling();
  1049. this.pollTimer = setInterval(() => {
  1050. this.forAllChangedKeys((key, oldValue, newValue) => {
  1051. this.onStorageEvent(new StorageEvent('storage', {
  1052. key,
  1053. oldValue,
  1054. newValue
  1055. }),
  1056. /* poll */ true);
  1057. });
  1058. }, _POLLING_INTERVAL_MS);
  1059. }
  1060. stopPolling() {
  1061. if (this.pollTimer) {
  1062. clearInterval(this.pollTimer);
  1063. this.pollTimer = null;
  1064. }
  1065. }
  1066. attachListener() {
  1067. window.addEventListener('storage', this.boundEventHandler);
  1068. }
  1069. detachListener() {
  1070. window.removeEventListener('storage', this.boundEventHandler);
  1071. }
  1072. _addListener(key, listener) {
  1073. if (Object.keys(this.listeners).length === 0) {
  1074. // Whether browser can detect storage event when it had already been pushed to the background.
  1075. // This may happen in some mobile browsers. A localStorage change in the foreground window
  1076. // will not be detected in the background window via the storage event.
  1077. // This was detected in iOS 7.x mobile browsers
  1078. if (this.fallbackToPolling) {
  1079. this.startPolling();
  1080. }
  1081. else {
  1082. this.attachListener();
  1083. }
  1084. }
  1085. if (!this.listeners[key]) {
  1086. this.listeners[key] = new Set();
  1087. // Populate the cache to avoid spuriously triggering on first poll.
  1088. this.localCache[key] = this.storage.getItem(key);
  1089. }
  1090. this.listeners[key].add(listener);
  1091. }
  1092. _removeListener(key, listener) {
  1093. if (this.listeners[key]) {
  1094. this.listeners[key].delete(listener);
  1095. if (this.listeners[key].size === 0) {
  1096. delete this.listeners[key];
  1097. }
  1098. }
  1099. if (Object.keys(this.listeners).length === 0) {
  1100. this.detachListener();
  1101. this.stopPolling();
  1102. }
  1103. }
  1104. // Update local cache on base operations:
  1105. async _set(key, value) {
  1106. await super._set(key, value);
  1107. this.localCache[key] = JSON.stringify(value);
  1108. }
  1109. async _get(key) {
  1110. const value = await super._get(key);
  1111. this.localCache[key] = JSON.stringify(value);
  1112. return value;
  1113. }
  1114. async _remove(key) {
  1115. await super._remove(key);
  1116. delete this.localCache[key];
  1117. }
  1118. }
  1119. BrowserLocalPersistence.type = 'LOCAL';
  1120. /**
  1121. * An implementation of {@link Persistence} of type `LOCAL` using `localStorage`
  1122. * for the underlying storage.
  1123. *
  1124. * @public
  1125. */
  1126. const browserLocalPersistence = BrowserLocalPersistence;
  1127. /**
  1128. * @license
  1129. * Copyright 2020 Google LLC
  1130. *
  1131. * Licensed under the Apache License, Version 2.0 (the "License");
  1132. * you may not use this file except in compliance with the License.
  1133. * You may obtain a copy of the License at
  1134. *
  1135. * http://www.apache.org/licenses/LICENSE-2.0
  1136. *
  1137. * Unless required by applicable law or agreed to in writing, software
  1138. * distributed under the License is distributed on an "AS IS" BASIS,
  1139. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1140. * See the License for the specific language governing permissions and
  1141. * limitations under the License.
  1142. */
  1143. const SESSION_ID_LENGTH = 20;
  1144. /** Custom AuthEventManager that adds passive listeners to events */
  1145. class CordovaAuthEventManager extends AuthEventManager {
  1146. constructor() {
  1147. super(...arguments);
  1148. this.passiveListeners = new Set();
  1149. this.initPromise = new Promise(resolve => {
  1150. this.resolveInialized = resolve;
  1151. });
  1152. }
  1153. addPassiveListener(cb) {
  1154. this.passiveListeners.add(cb);
  1155. }
  1156. removePassiveListener(cb) {
  1157. this.passiveListeners.delete(cb);
  1158. }
  1159. // In a Cordova environment, this manager can live through multiple redirect
  1160. // operations
  1161. resetRedirect() {
  1162. this.queuedRedirectEvent = null;
  1163. this.hasHandledPotentialRedirect = false;
  1164. }
  1165. /** Override the onEvent method */
  1166. onEvent(event) {
  1167. this.resolveInialized();
  1168. this.passiveListeners.forEach(cb => cb(event));
  1169. return super.onEvent(event);
  1170. }
  1171. async initialized() {
  1172. await this.initPromise;
  1173. }
  1174. }
  1175. /**
  1176. * Generates a (partial) {@link AuthEvent}.
  1177. */
  1178. function _generateNewEvent(auth, type, eventId = null) {
  1179. return {
  1180. type,
  1181. eventId,
  1182. urlResponse: null,
  1183. sessionId: generateSessionId(),
  1184. postBody: null,
  1185. tenantId: auth.tenantId,
  1186. error: _createError(auth, "no-auth-event" /* AuthErrorCode.NO_AUTH_EVENT */)
  1187. };
  1188. }
  1189. function _savePartialEvent(auth, event) {
  1190. return storage()._set(persistenceKey(auth), event);
  1191. }
  1192. async function _getAndRemoveEvent(auth) {
  1193. const event = (await storage()._get(persistenceKey(auth)));
  1194. if (event) {
  1195. await storage()._remove(persistenceKey(auth));
  1196. }
  1197. return event;
  1198. }
  1199. function _eventFromPartialAndUrl(partialEvent, url) {
  1200. var _a, _b;
  1201. // Parse the deep link within the dynamic link URL.
  1202. const callbackUrl = _getDeepLinkFromCallback(url);
  1203. // Confirm it is actually a callback URL.
  1204. // Currently the universal link will be of this format:
  1205. // https://<AUTH_DOMAIN>/__/auth/callback<OAUTH_RESPONSE>
  1206. // This is a fake URL but is not intended to take the user anywhere
  1207. // and just redirect to the app.
  1208. if (callbackUrl.includes('/__/auth/callback')) {
  1209. // Check if there is an error in the URL.
  1210. // This mechanism is also used to pass errors back to the app:
  1211. // https://<AUTH_DOMAIN>/__/auth/callback?firebaseError=<STRINGIFIED_ERROR>
  1212. const params = searchParamsOrEmpty(callbackUrl);
  1213. // Get the error object corresponding to the stringified error if found.
  1214. const errorObject = params['firebaseError']
  1215. ? parseJsonOrNull(decodeURIComponent(params['firebaseError']))
  1216. : null;
  1217. const code = (_b = (_a = errorObject === null || errorObject === void 0 ? void 0 : errorObject['code']) === null || _a === void 0 ? void 0 : _a.split('auth/')) === null || _b === void 0 ? void 0 : _b[1];
  1218. const error = code ? _createError(code) : null;
  1219. if (error) {
  1220. return {
  1221. type: partialEvent.type,
  1222. eventId: partialEvent.eventId,
  1223. tenantId: partialEvent.tenantId,
  1224. error,
  1225. urlResponse: null,
  1226. sessionId: null,
  1227. postBody: null
  1228. };
  1229. }
  1230. else {
  1231. return {
  1232. type: partialEvent.type,
  1233. eventId: partialEvent.eventId,
  1234. tenantId: partialEvent.tenantId,
  1235. sessionId: partialEvent.sessionId,
  1236. urlResponse: callbackUrl,
  1237. postBody: null
  1238. };
  1239. }
  1240. }
  1241. return null;
  1242. }
  1243. function generateSessionId() {
  1244. const chars = [];
  1245. const allowedChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  1246. for (let i = 0; i < SESSION_ID_LENGTH; i++) {
  1247. const idx = Math.floor(Math.random() * allowedChars.length);
  1248. chars.push(allowedChars.charAt(idx));
  1249. }
  1250. return chars.join('');
  1251. }
  1252. function storage() {
  1253. return _getInstance(browserLocalPersistence);
  1254. }
  1255. function persistenceKey(auth) {
  1256. return _persistenceKeyName("authEvent" /* KeyName.AUTH_EVENT */, auth.config.apiKey, auth.name);
  1257. }
  1258. function parseJsonOrNull(json) {
  1259. try {
  1260. return JSON.parse(json);
  1261. }
  1262. catch (e) {
  1263. return null;
  1264. }
  1265. }
  1266. // Exported for testing
  1267. function _getDeepLinkFromCallback(url) {
  1268. const params = searchParamsOrEmpty(url);
  1269. const link = params['link'] ? decodeURIComponent(params['link']) : undefined;
  1270. // Double link case (automatic redirect)
  1271. const doubleDeepLink = searchParamsOrEmpty(link)['link'];
  1272. // iOS custom scheme links.
  1273. const iOSDeepLink = params['deep_link_id']
  1274. ? decodeURIComponent(params['deep_link_id'])
  1275. : undefined;
  1276. const iOSDoubleDeepLink = searchParamsOrEmpty(iOSDeepLink)['link'];
  1277. return iOSDoubleDeepLink || iOSDeepLink || doubleDeepLink || link || url;
  1278. }
  1279. /**
  1280. * Optimistically tries to get search params from a string, or else returns an
  1281. * empty search params object.
  1282. */
  1283. function searchParamsOrEmpty(url) {
  1284. if (!(url === null || url === void 0 ? void 0 : url.includes('?'))) {
  1285. return {};
  1286. }
  1287. const [_, ...rest] = url.split('?');
  1288. return querystringDecode(rest.join('?'));
  1289. }
  1290. /**
  1291. * @license
  1292. * Copyright 2021 Google LLC
  1293. *
  1294. * Licensed under the Apache License, Version 2.0 (the "License");
  1295. * you may not use this file except in compliance with the License.
  1296. * You may obtain a copy of the License at
  1297. *
  1298. * http://www.apache.org/licenses/LICENSE-2.0
  1299. *
  1300. * Unless required by applicable law or agreed to in writing, software
  1301. * distributed under the License is distributed on an "AS IS" BASIS,
  1302. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1303. * See the License for the specific language governing permissions and
  1304. * limitations under the License.
  1305. */
  1306. /**
  1307. * How long to wait for the initial auth event before concluding no
  1308. * redirect pending
  1309. */
  1310. const INITIAL_EVENT_TIMEOUT_MS = 500;
  1311. class CordovaPopupRedirectResolver {
  1312. constructor() {
  1313. this._redirectPersistence = browserSessionPersistence;
  1314. this._shouldInitProactively = true; // This is lightweight for Cordova
  1315. this.eventManagers = new Map();
  1316. this.originValidationPromises = {};
  1317. this._completeRedirectFn = _getRedirectResult;
  1318. this._overrideRedirectResult = _overrideRedirectResult;
  1319. }
  1320. async _initialize(auth) {
  1321. const key = auth._key();
  1322. let manager = this.eventManagers.get(key);
  1323. if (!manager) {
  1324. manager = new CordovaAuthEventManager(auth);
  1325. this.eventManagers.set(key, manager);
  1326. this.attachCallbackListeners(auth, manager);
  1327. }
  1328. return manager;
  1329. }
  1330. _openPopup(auth) {
  1331. _fail(auth, "operation-not-supported-in-this-environment" /* AuthErrorCode.OPERATION_NOT_SUPPORTED */);
  1332. }
  1333. async _openRedirect(auth, provider, authType, eventId) {
  1334. _checkCordovaConfiguration(auth);
  1335. const manager = await this._initialize(auth);
  1336. await manager.initialized();
  1337. // Reset the persisted redirect states. This does not matter on Web where
  1338. // the redirect always blows away application state entirely. On Cordova,
  1339. // the app maintains control flow through the redirect.
  1340. manager.resetRedirect();
  1341. _clearRedirectOutcomes();
  1342. await this._originValidation(auth);
  1343. const event = _generateNewEvent(auth, authType, eventId);
  1344. await _savePartialEvent(auth, event);
  1345. const url = await _generateHandlerUrl(auth, event, provider);
  1346. const iabRef = await _performRedirect(url);
  1347. return _waitForAppResume(auth, manager, iabRef);
  1348. }
  1349. _isIframeWebStorageSupported(_auth, _cb) {
  1350. throw new Error('Method not implemented.');
  1351. }
  1352. _originValidation(auth) {
  1353. const key = auth._key();
  1354. if (!this.originValidationPromises[key]) {
  1355. this.originValidationPromises[key] = _validateOrigin(auth);
  1356. }
  1357. return this.originValidationPromises[key];
  1358. }
  1359. attachCallbackListeners(auth, manager) {
  1360. // Get the global plugins
  1361. const { universalLinks, handleOpenURL, BuildInfo } = _cordovaWindow();
  1362. const noEventTimeout = setTimeout(async () => {
  1363. // We didn't see that initial event. Clear any pending object and
  1364. // dispatch no event
  1365. await _getAndRemoveEvent(auth);
  1366. manager.onEvent(generateNoEvent());
  1367. }, INITIAL_EVENT_TIMEOUT_MS);
  1368. const universalLinksCb = async (eventData) => {
  1369. // We have an event so we can clear the no event timeout
  1370. clearTimeout(noEventTimeout);
  1371. const partialEvent = await _getAndRemoveEvent(auth);
  1372. let finalEvent = null;
  1373. if (partialEvent && (eventData === null || eventData === void 0 ? void 0 : eventData['url'])) {
  1374. finalEvent = _eventFromPartialAndUrl(partialEvent, eventData['url']);
  1375. }
  1376. // If finalEvent is never filled, trigger with no event
  1377. manager.onEvent(finalEvent || generateNoEvent());
  1378. };
  1379. // Universal links subscriber doesn't exist for iOS, so we need to check
  1380. if (typeof universalLinks !== 'undefined' &&
  1381. typeof universalLinks.subscribe === 'function') {
  1382. universalLinks.subscribe(null, universalLinksCb);
  1383. }
  1384. // iOS 7 or 8 custom URL schemes.
  1385. // This is also the current default behavior for iOS 9+.
  1386. // For this to work, cordova-plugin-customurlscheme needs to be installed.
  1387. // https://github.com/EddyVerbruggen/Custom-URL-scheme
  1388. // Do not overwrite the existing developer's URL handler.
  1389. const existingHandleOpenURL = handleOpenURL;
  1390. const packagePrefix = `${BuildInfo.packageName.toLowerCase()}://`;
  1391. _cordovaWindow().handleOpenURL = async (url) => {
  1392. if (url.toLowerCase().startsWith(packagePrefix)) {
  1393. // We want this intentionally to float
  1394. // eslint-disable-next-line @typescript-eslint/no-floating-promises
  1395. universalLinksCb({ url });
  1396. }
  1397. // Call the developer's handler if it is present.
  1398. if (typeof existingHandleOpenURL === 'function') {
  1399. try {
  1400. existingHandleOpenURL(url);
  1401. }
  1402. catch (e) {
  1403. // This is a developer error. Don't stop the flow of the SDK.
  1404. console.error(e);
  1405. }
  1406. }
  1407. };
  1408. }
  1409. }
  1410. /**
  1411. * An implementation of {@link PopupRedirectResolver} suitable for Cordova
  1412. * based applications.
  1413. *
  1414. * @public
  1415. */
  1416. const cordovaPopupRedirectResolver = CordovaPopupRedirectResolver;
  1417. function generateNoEvent() {
  1418. return {
  1419. type: "unknown" /* AuthEventType.UNKNOWN */,
  1420. eventId: null,
  1421. sessionId: null,
  1422. urlResponse: null,
  1423. postBody: null,
  1424. tenantId: null,
  1425. error: _createError("no-auth-event" /* AuthErrorCode.NO_AUTH_EVENT */)
  1426. };
  1427. }
  1428. /**
  1429. * @license
  1430. * Copyright 2017 Google LLC
  1431. *
  1432. * Licensed under the Apache License, Version 2.0 (the "License");
  1433. * you may not use this file except in compliance with the License.
  1434. * You may obtain a copy of the License at
  1435. *
  1436. * http://www.apache.org/licenses/LICENSE-2.0
  1437. *
  1438. * Unless required by applicable law or agreed to in writing, software
  1439. * distributed under the License is distributed on an "AS IS" BASIS,
  1440. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1441. * See the License for the specific language governing permissions and
  1442. * limitations under the License.
  1443. */
  1444. // This function should only be called by frameworks (e.g. FirebaseUI-web) to log their usage.
  1445. // It is not intended for direct use by developer apps. NO jsdoc here to intentionally leave it out
  1446. // of autogenerated documentation pages to reduce accidental misuse.
  1447. function addFrameworkForLogging(auth, framework) {
  1448. _castAuth(auth)._logFramework(framework);
  1449. }
  1450. export { AuthPopup, _generateEventId, _getRedirectResult, _overrideRedirectResult, addFrameworkForLogging, cordovaPopupRedirectResolver };
  1451. //# sourceMappingURL=internal.js.map