GSI - Employe Self Service Mobile
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1768 lines
72 KiB

2 months ago
  1. import { _getProvider, getApp, _registerComponent, registerVersion } from '@firebase/app';
  2. import { Component } from '@firebase/component';
  3. import { __assign, __awaiter, __generator, __spreadArray } from 'tslib';
  4. import { Deferred, ErrorFactory, isIndexedDBAvailable, uuidv4, getGlobal, base64, issuedAtTime, calculateBackoffMillis, getModularInstance } from '@firebase/util';
  5. import { Logger } from '@firebase/logger';
  6. /**
  7. * @license
  8. * Copyright 2020 Google LLC
  9. *
  10. * Licensed under the Apache License, Version 2.0 (the "License");
  11. * you may not use this file except in compliance with the License.
  12. * You may obtain a copy of the License at
  13. *
  14. * http://www.apache.org/licenses/LICENSE-2.0
  15. *
  16. * Unless required by applicable law or agreed to in writing, software
  17. * distributed under the License is distributed on an "AS IS" BASIS,
  18. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. * See the License for the specific language governing permissions and
  20. * limitations under the License.
  21. */
  22. var APP_CHECK_STATES = new Map();
  23. var DEFAULT_STATE = {
  24. activated: false,
  25. tokenObservers: []
  26. };
  27. var DEBUG_STATE = {
  28. initialized: false,
  29. enabled: false
  30. };
  31. /**
  32. * Gets a reference to the state object.
  33. */
  34. function getStateReference(app) {
  35. return APP_CHECK_STATES.get(app) || __assign({}, DEFAULT_STATE);
  36. }
  37. /**
  38. * Set once on initialization. The map should hold the same reference to the
  39. * same object until this entry is deleted.
  40. */
  41. function setInitialState(app, state) {
  42. APP_CHECK_STATES.set(app, state);
  43. return APP_CHECK_STATES.get(app);
  44. }
  45. function getDebugState() {
  46. return DEBUG_STATE;
  47. }
  48. /**
  49. * @license
  50. * Copyright 2020 Google LLC
  51. *
  52. * Licensed under the Apache License, Version 2.0 (the "License");
  53. * you may not use this file except in compliance with the License.
  54. * You may obtain a copy of the License at
  55. *
  56. * http://www.apache.org/licenses/LICENSE-2.0
  57. *
  58. * Unless required by applicable law or agreed to in writing, software
  59. * distributed under the License is distributed on an "AS IS" BASIS,
  60. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  61. * See the License for the specific language governing permissions and
  62. * limitations under the License.
  63. */
  64. var BASE_ENDPOINT = 'https://content-firebaseappcheck.googleapis.com/v1';
  65. var EXCHANGE_RECAPTCHA_TOKEN_METHOD = 'exchangeRecaptchaV3Token';
  66. var EXCHANGE_RECAPTCHA_ENTERPRISE_TOKEN_METHOD = 'exchangeRecaptchaEnterpriseToken';
  67. var EXCHANGE_DEBUG_TOKEN_METHOD = 'exchangeDebugToken';
  68. var TOKEN_REFRESH_TIME = {
  69. /**
  70. * The offset time before token natural expiration to run the refresh.
  71. * This is currently 5 minutes.
  72. */
  73. OFFSET_DURATION: 5 * 60 * 1000,
  74. /**
  75. * This is the first retrial wait after an error. This is currently
  76. * 30 seconds.
  77. */
  78. RETRIAL_MIN_WAIT: 30 * 1000,
  79. /**
  80. * This is the maximum retrial wait, currently 16 minutes.
  81. */
  82. RETRIAL_MAX_WAIT: 16 * 60 * 1000
  83. };
  84. /**
  85. * One day in millis, for certain error code backoffs.
  86. */
  87. var ONE_DAY = 24 * 60 * 60 * 1000;
  88. /**
  89. * @license
  90. * Copyright 2020 Google LLC
  91. *
  92. * Licensed under the Apache License, Version 2.0 (the "License");
  93. * you may not use this file except in compliance with the License.
  94. * You may obtain a copy of the License at
  95. *
  96. * http://www.apache.org/licenses/LICENSE-2.0
  97. *
  98. * Unless required by applicable law or agreed to in writing, software
  99. * distributed under the License is distributed on an "AS IS" BASIS,
  100. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  101. * See the License for the specific language governing permissions and
  102. * limitations under the License.
  103. */
  104. /**
  105. * Port from auth proactiverefresh.js
  106. *
  107. */
  108. // TODO: move it to @firebase/util?
  109. // TODO: allow to config whether refresh should happen in the background
  110. var Refresher = /** @class */ (function () {
  111. function Refresher(operation, retryPolicy, getWaitDuration, lowerBound, upperBound) {
  112. this.operation = operation;
  113. this.retryPolicy = retryPolicy;
  114. this.getWaitDuration = getWaitDuration;
  115. this.lowerBound = lowerBound;
  116. this.upperBound = upperBound;
  117. this.pending = null;
  118. this.nextErrorWaitInterval = lowerBound;
  119. if (lowerBound > upperBound) {
  120. throw new Error('Proactive refresh lower bound greater than upper bound!');
  121. }
  122. }
  123. Refresher.prototype.start = function () {
  124. this.nextErrorWaitInterval = this.lowerBound;
  125. this.process(true).catch(function () {
  126. /* we don't care about the result */
  127. });
  128. };
  129. Refresher.prototype.stop = function () {
  130. if (this.pending) {
  131. this.pending.reject('cancelled');
  132. this.pending = null;
  133. }
  134. };
  135. Refresher.prototype.isRunning = function () {
  136. return !!this.pending;
  137. };
  138. Refresher.prototype.process = function (hasSucceeded) {
  139. return __awaiter(this, void 0, void 0, function () {
  140. var error_1;
  141. return __generator(this, function (_a) {
  142. switch (_a.label) {
  143. case 0:
  144. this.stop();
  145. _a.label = 1;
  146. case 1:
  147. _a.trys.push([1, 6, , 7]);
  148. this.pending = new Deferred();
  149. return [4 /*yield*/, sleep(this.getNextRun(hasSucceeded))];
  150. case 2:
  151. _a.sent();
  152. // Why do we resolve a promise, then immediate wait for it?
  153. // We do it to make the promise chain cancellable.
  154. // We can call stop() which rejects the promise before the following line execute, which makes
  155. // the code jump to the catch block.
  156. // TODO: unit test this
  157. this.pending.resolve();
  158. return [4 /*yield*/, this.pending.promise];
  159. case 3:
  160. _a.sent();
  161. this.pending = new Deferred();
  162. return [4 /*yield*/, this.operation()];
  163. case 4:
  164. _a.sent();
  165. this.pending.resolve();
  166. return [4 /*yield*/, this.pending.promise];
  167. case 5:
  168. _a.sent();
  169. this.process(true).catch(function () {
  170. /* we don't care about the result */
  171. });
  172. return [3 /*break*/, 7];
  173. case 6:
  174. error_1 = _a.sent();
  175. if (this.retryPolicy(error_1)) {
  176. this.process(false).catch(function () {
  177. /* we don't care about the result */
  178. });
  179. }
  180. else {
  181. this.stop();
  182. }
  183. return [3 /*break*/, 7];
  184. case 7: return [2 /*return*/];
  185. }
  186. });
  187. });
  188. };
  189. Refresher.prototype.getNextRun = function (hasSucceeded) {
  190. if (hasSucceeded) {
  191. // If last operation succeeded, reset next error wait interval and return
  192. // the default wait duration.
  193. this.nextErrorWaitInterval = this.lowerBound;
  194. // Return typical wait duration interval after a successful operation.
  195. return this.getWaitDuration();
  196. }
  197. else {
  198. // Get next error wait interval.
  199. var currentErrorWaitInterval = this.nextErrorWaitInterval;
  200. // Double interval for next consecutive error.
  201. this.nextErrorWaitInterval *= 2;
  202. // Make sure next wait interval does not exceed the maximum upper bound.
  203. if (this.nextErrorWaitInterval > this.upperBound) {
  204. this.nextErrorWaitInterval = this.upperBound;
  205. }
  206. return currentErrorWaitInterval;
  207. }
  208. };
  209. return Refresher;
  210. }());
  211. function sleep(ms) {
  212. return new Promise(function (resolve) {
  213. setTimeout(resolve, ms);
  214. });
  215. }
  216. /**
  217. * @license
  218. * Copyright 2020 Google LLC
  219. *
  220. * Licensed under the Apache License, Version 2.0 (the "License");
  221. * you may not use this file except in compliance with the License.
  222. * You may obtain a copy of the License at
  223. *
  224. * http://www.apache.org/licenses/LICENSE-2.0
  225. *
  226. * Unless required by applicable law or agreed to in writing, software
  227. * distributed under the License is distributed on an "AS IS" BASIS,
  228. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  229. * See the License for the specific language governing permissions and
  230. * limitations under the License.
  231. */
  232. var _a;
  233. var ERRORS = (_a = {},
  234. _a["already-initialized" /* AppCheckError.ALREADY_INITIALIZED */] = 'You have already called initializeAppCheck() for FirebaseApp {$appName} with ' +
  235. 'different options. To avoid this error, call initializeAppCheck() with the ' +
  236. 'same options as when it was originally called. This will return the ' +
  237. 'already initialized instance.',
  238. _a["use-before-activation" /* AppCheckError.USE_BEFORE_ACTIVATION */] = 'App Check is being used before initializeAppCheck() is called for FirebaseApp {$appName}. ' +
  239. 'Call initializeAppCheck() before instantiating other Firebase services.',
  240. _a["fetch-network-error" /* AppCheckError.FETCH_NETWORK_ERROR */] = 'Fetch failed to connect to a network. Check Internet connection. ' +
  241. 'Original error: {$originalErrorMessage}.',
  242. _a["fetch-parse-error" /* AppCheckError.FETCH_PARSE_ERROR */] = 'Fetch client could not parse response.' +
  243. ' Original error: {$originalErrorMessage}.',
  244. _a["fetch-status-error" /* AppCheckError.FETCH_STATUS_ERROR */] = 'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.',
  245. _a["storage-open" /* AppCheckError.STORAGE_OPEN */] = 'Error thrown when opening storage. Original error: {$originalErrorMessage}.',
  246. _a["storage-get" /* AppCheckError.STORAGE_GET */] = 'Error thrown when reading from storage. Original error: {$originalErrorMessage}.',
  247. _a["storage-set" /* AppCheckError.STORAGE_WRITE */] = 'Error thrown when writing to storage. Original error: {$originalErrorMessage}.',
  248. _a["recaptcha-error" /* AppCheckError.RECAPTCHA_ERROR */] = 'ReCAPTCHA error.',
  249. _a["throttled" /* AppCheckError.THROTTLED */] = "Requests throttled due to {$httpStatus} error. Attempts allowed again after {$time}",
  250. _a);
  251. var ERROR_FACTORY = new ErrorFactory('appCheck', 'AppCheck', ERRORS);
  252. /**
  253. * @license
  254. * Copyright 2020 Google LLC
  255. *
  256. * Licensed under the Apache License, Version 2.0 (the "License");
  257. * you may not use this file except in compliance with the License.
  258. * You may obtain a copy of the License at
  259. *
  260. * http://www.apache.org/licenses/LICENSE-2.0
  261. *
  262. * Unless required by applicable law or agreed to in writing, software
  263. * distributed under the License is distributed on an "AS IS" BASIS,
  264. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  265. * See the License for the specific language governing permissions and
  266. * limitations under the License.
  267. */
  268. function getRecaptcha(isEnterprise) {
  269. var _a;
  270. if (isEnterprise === void 0) { isEnterprise = false; }
  271. if (isEnterprise) {
  272. return (_a = self.grecaptcha) === null || _a === void 0 ? void 0 : _a.enterprise;
  273. }
  274. return self.grecaptcha;
  275. }
  276. function ensureActivated(app) {
  277. if (!getStateReference(app).activated) {
  278. throw ERROR_FACTORY.create("use-before-activation" /* AppCheckError.USE_BEFORE_ACTIVATION */, {
  279. appName: app.name
  280. });
  281. }
  282. }
  283. function getDurationString(durationInMillis) {
  284. var totalSeconds = Math.round(durationInMillis / 1000);
  285. var days = Math.floor(totalSeconds / (3600 * 24));
  286. var hours = Math.floor((totalSeconds - days * 3600 * 24) / 3600);
  287. var minutes = Math.floor((totalSeconds - days * 3600 * 24 - hours * 3600) / 60);
  288. var seconds = totalSeconds - days * 3600 * 24 - hours * 3600 - minutes * 60;
  289. var result = '';
  290. if (days) {
  291. result += pad(days) + 'd:';
  292. }
  293. if (hours) {
  294. result += pad(hours) + 'h:';
  295. }
  296. result += pad(minutes) + 'm:' + pad(seconds) + 's';
  297. return result;
  298. }
  299. function pad(value) {
  300. if (value === 0) {
  301. return '00';
  302. }
  303. return value >= 10 ? value.toString() : '0' + value;
  304. }
  305. /**
  306. * @license
  307. * Copyright 2020 Google LLC
  308. *
  309. * Licensed under the Apache License, Version 2.0 (the "License");
  310. * you may not use this file except in compliance with the License.
  311. * You may obtain a copy of the License at
  312. *
  313. * http://www.apache.org/licenses/LICENSE-2.0
  314. *
  315. * Unless required by applicable law or agreed to in writing, software
  316. * distributed under the License is distributed on an "AS IS" BASIS,
  317. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  318. * See the License for the specific language governing permissions and
  319. * limitations under the License.
  320. */
  321. function exchangeToken(_a, heartbeatServiceProvider) {
  322. var url = _a.url, body = _a.body;
  323. return __awaiter(this, void 0, void 0, function () {
  324. var headers, heartbeatService, heartbeatsHeader, options, response, originalError_1, responseBody, originalError_2, match, timeToLiveAsNumber, now;
  325. return __generator(this, function (_b) {
  326. switch (_b.label) {
  327. case 0:
  328. headers = {
  329. 'Content-Type': 'application/json'
  330. };
  331. heartbeatService = heartbeatServiceProvider.getImmediate({
  332. optional: true
  333. });
  334. if (!heartbeatService) return [3 /*break*/, 2];
  335. return [4 /*yield*/, heartbeatService.getHeartbeatsHeader()];
  336. case 1:
  337. heartbeatsHeader = _b.sent();
  338. if (heartbeatsHeader) {
  339. headers['X-Firebase-Client'] = heartbeatsHeader;
  340. }
  341. _b.label = 2;
  342. case 2:
  343. options = {
  344. method: 'POST',
  345. body: JSON.stringify(body),
  346. headers: headers
  347. };
  348. _b.label = 3;
  349. case 3:
  350. _b.trys.push([3, 5, , 6]);
  351. return [4 /*yield*/, fetch(url, options)];
  352. case 4:
  353. response = _b.sent();
  354. return [3 /*break*/, 6];
  355. case 5:
  356. originalError_1 = _b.sent();
  357. throw ERROR_FACTORY.create("fetch-network-error" /* AppCheckError.FETCH_NETWORK_ERROR */, {
  358. originalErrorMessage: originalError_1 === null || originalError_1 === void 0 ? void 0 : originalError_1.message
  359. });
  360. case 6:
  361. if (response.status !== 200) {
  362. throw ERROR_FACTORY.create("fetch-status-error" /* AppCheckError.FETCH_STATUS_ERROR */, {
  363. httpStatus: response.status
  364. });
  365. }
  366. _b.label = 7;
  367. case 7:
  368. _b.trys.push([7, 9, , 10]);
  369. return [4 /*yield*/, response.json()];
  370. case 8:
  371. // JSON parsing throws SyntaxError if the response body isn't a JSON string.
  372. responseBody = _b.sent();
  373. return [3 /*break*/, 10];
  374. case 9:
  375. originalError_2 = _b.sent();
  376. throw ERROR_FACTORY.create("fetch-parse-error" /* AppCheckError.FETCH_PARSE_ERROR */, {
  377. originalErrorMessage: originalError_2 === null || originalError_2 === void 0 ? void 0 : originalError_2.message
  378. });
  379. case 10:
  380. match = responseBody.ttl.match(/^([\d.]+)(s)$/);
  381. if (!match || !match[2] || isNaN(Number(match[1]))) {
  382. throw ERROR_FACTORY.create("fetch-parse-error" /* AppCheckError.FETCH_PARSE_ERROR */, {
  383. originalErrorMessage: "ttl field (timeToLive) is not in standard Protobuf Duration " +
  384. "format: ".concat(responseBody.ttl)
  385. });
  386. }
  387. timeToLiveAsNumber = Number(match[1]) * 1000;
  388. now = Date.now();
  389. return [2 /*return*/, {
  390. token: responseBody.token,
  391. expireTimeMillis: now + timeToLiveAsNumber,
  392. issuedAtTimeMillis: now
  393. }];
  394. }
  395. });
  396. });
  397. }
  398. function getExchangeRecaptchaV3TokenRequest(app, reCAPTCHAToken) {
  399. var _a = app.options, projectId = _a.projectId, appId = _a.appId, apiKey = _a.apiKey;
  400. return {
  401. url: "".concat(BASE_ENDPOINT, "/projects/").concat(projectId, "/apps/").concat(appId, ":").concat(EXCHANGE_RECAPTCHA_TOKEN_METHOD, "?key=").concat(apiKey),
  402. body: {
  403. 'recaptcha_v3_token': reCAPTCHAToken
  404. }
  405. };
  406. }
  407. function getExchangeRecaptchaEnterpriseTokenRequest(app, reCAPTCHAToken) {
  408. var _a = app.options, projectId = _a.projectId, appId = _a.appId, apiKey = _a.apiKey;
  409. return {
  410. url: "".concat(BASE_ENDPOINT, "/projects/").concat(projectId, "/apps/").concat(appId, ":").concat(EXCHANGE_RECAPTCHA_ENTERPRISE_TOKEN_METHOD, "?key=").concat(apiKey),
  411. body: {
  412. 'recaptcha_enterprise_token': reCAPTCHAToken
  413. }
  414. };
  415. }
  416. function getExchangeDebugTokenRequest(app, debugToken) {
  417. var _a = app.options, projectId = _a.projectId, appId = _a.appId, apiKey = _a.apiKey;
  418. return {
  419. url: "".concat(BASE_ENDPOINT, "/projects/").concat(projectId, "/apps/").concat(appId, ":").concat(EXCHANGE_DEBUG_TOKEN_METHOD, "?key=").concat(apiKey),
  420. body: {
  421. // eslint-disable-next-line
  422. debug_token: debugToken
  423. }
  424. };
  425. }
  426. /**
  427. * @license
  428. * Copyright 2020 Google LLC
  429. *
  430. * Licensed under the Apache License, Version 2.0 (the "License");
  431. * you may not use this file except in compliance with the License.
  432. * You may obtain a copy of the License at
  433. *
  434. * http://www.apache.org/licenses/LICENSE-2.0
  435. *
  436. * Unless required by applicable law or agreed to in writing, software
  437. * distributed under the License is distributed on an "AS IS" BASIS,
  438. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  439. * See the License for the specific language governing permissions and
  440. * limitations under the License.
  441. */
  442. var DB_NAME = 'firebase-app-check-database';
  443. var DB_VERSION = 1;
  444. var STORE_NAME = 'firebase-app-check-store';
  445. var DEBUG_TOKEN_KEY = 'debug-token';
  446. var dbPromise = null;
  447. function getDBPromise() {
  448. if (dbPromise) {
  449. return dbPromise;
  450. }
  451. dbPromise = new Promise(function (resolve, reject) {
  452. try {
  453. var request = indexedDB.open(DB_NAME, DB_VERSION);
  454. request.onsuccess = function (event) {
  455. resolve(event.target.result);
  456. };
  457. request.onerror = function (event) {
  458. var _a;
  459. reject(ERROR_FACTORY.create("storage-open" /* AppCheckError.STORAGE_OPEN */, {
  460. originalErrorMessage: (_a = event.target.error) === null || _a === void 0 ? void 0 : _a.message
  461. }));
  462. };
  463. request.onupgradeneeded = function (event) {
  464. var db = event.target.result;
  465. // We don't use 'break' in this switch statement, the fall-through
  466. // behavior is what we want, because if there are multiple versions between
  467. // the old version and the current version, we want ALL the migrations
  468. // that correspond to those versions to run, not only the last one.
  469. // eslint-disable-next-line default-case
  470. switch (event.oldVersion) {
  471. case 0:
  472. db.createObjectStore(STORE_NAME, {
  473. keyPath: 'compositeKey'
  474. });
  475. }
  476. };
  477. }
  478. catch (e) {
  479. reject(ERROR_FACTORY.create("storage-open" /* AppCheckError.STORAGE_OPEN */, {
  480. originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
  481. }));
  482. }
  483. });
  484. return dbPromise;
  485. }
  486. function readTokenFromIndexedDB(app) {
  487. return read(computeKey(app));
  488. }
  489. function writeTokenToIndexedDB(app, token) {
  490. return write(computeKey(app), token);
  491. }
  492. function writeDebugTokenToIndexedDB(token) {
  493. return write(DEBUG_TOKEN_KEY, token);
  494. }
  495. function readDebugTokenFromIndexedDB() {
  496. return read(DEBUG_TOKEN_KEY);
  497. }
  498. function write(key, value) {
  499. return __awaiter(this, void 0, void 0, function () {
  500. var db, transaction, store, request;
  501. return __generator(this, function (_a) {
  502. switch (_a.label) {
  503. case 0: return [4 /*yield*/, getDBPromise()];
  504. case 1:
  505. db = _a.sent();
  506. transaction = db.transaction(STORE_NAME, 'readwrite');
  507. store = transaction.objectStore(STORE_NAME);
  508. request = store.put({
  509. compositeKey: key,
  510. value: value
  511. });
  512. return [2 /*return*/, new Promise(function (resolve, reject) {
  513. request.onsuccess = function (_event) {
  514. resolve();
  515. };
  516. transaction.onerror = function (event) {
  517. var _a;
  518. reject(ERROR_FACTORY.create("storage-set" /* AppCheckError.STORAGE_WRITE */, {
  519. originalErrorMessage: (_a = event.target.error) === null || _a === void 0 ? void 0 : _a.message
  520. }));
  521. };
  522. })];
  523. }
  524. });
  525. });
  526. }
  527. function read(key) {
  528. return __awaiter(this, void 0, void 0, function () {
  529. var db, transaction, store, request;
  530. return __generator(this, function (_a) {
  531. switch (_a.label) {
  532. case 0: return [4 /*yield*/, getDBPromise()];
  533. case 1:
  534. db = _a.sent();
  535. transaction = db.transaction(STORE_NAME, 'readonly');
  536. store = transaction.objectStore(STORE_NAME);
  537. request = store.get(key);
  538. return [2 /*return*/, new Promise(function (resolve, reject) {
  539. request.onsuccess = function (event) {
  540. var result = event.target.result;
  541. if (result) {
  542. resolve(result.value);
  543. }
  544. else {
  545. resolve(undefined);
  546. }
  547. };
  548. transaction.onerror = function (event) {
  549. var _a;
  550. reject(ERROR_FACTORY.create("storage-get" /* AppCheckError.STORAGE_GET */, {
  551. originalErrorMessage: (_a = event.target.error) === null || _a === void 0 ? void 0 : _a.message
  552. }));
  553. };
  554. })];
  555. }
  556. });
  557. });
  558. }
  559. function computeKey(app) {
  560. return "".concat(app.options.appId, "-").concat(app.name);
  561. }
  562. /**
  563. * @license
  564. * Copyright 2020 Google LLC
  565. *
  566. * Licensed under the Apache License, Version 2.0 (the "License");
  567. * you may not use this file except in compliance with the License.
  568. * You may obtain a copy of the License at
  569. *
  570. * http://www.apache.org/licenses/LICENSE-2.0
  571. *
  572. * Unless required by applicable law or agreed to in writing, software
  573. * distributed under the License is distributed on an "AS IS" BASIS,
  574. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  575. * See the License for the specific language governing permissions and
  576. * limitations under the License.
  577. */
  578. var logger = new Logger('@firebase/app-check');
  579. /**
  580. * @license
  581. * Copyright 2020 Google LLC
  582. *
  583. * Licensed under the Apache License, Version 2.0 (the "License");
  584. * you may not use this file except in compliance with the License.
  585. * You may obtain a copy of the License at
  586. *
  587. * http://www.apache.org/licenses/LICENSE-2.0
  588. *
  589. * Unless required by applicable law or agreed to in writing, software
  590. * distributed under the License is distributed on an "AS IS" BASIS,
  591. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  592. * See the License for the specific language governing permissions and
  593. * limitations under the License.
  594. */
  595. /**
  596. * Always resolves. In case of an error reading from indexeddb, resolve with undefined
  597. */
  598. function readTokenFromStorage(app) {
  599. return __awaiter(this, void 0, void 0, function () {
  600. var token, e_1;
  601. return __generator(this, function (_a) {
  602. switch (_a.label) {
  603. case 0:
  604. if (!isIndexedDBAvailable()) return [3 /*break*/, 5];
  605. token = undefined;
  606. _a.label = 1;
  607. case 1:
  608. _a.trys.push([1, 3, , 4]);
  609. return [4 /*yield*/, readTokenFromIndexedDB(app)];
  610. case 2:
  611. token = _a.sent();
  612. return [3 /*break*/, 4];
  613. case 3:
  614. e_1 = _a.sent();
  615. // swallow the error and return undefined
  616. logger.warn("Failed to read token from IndexedDB. Error: ".concat(e_1));
  617. return [3 /*break*/, 4];
  618. case 4: return [2 /*return*/, token];
  619. case 5: return [2 /*return*/, undefined];
  620. }
  621. });
  622. });
  623. }
  624. /**
  625. * Always resolves. In case of an error writing to indexeddb, print a warning and resolve the promise
  626. */
  627. function writeTokenToStorage(app, token) {
  628. if (isIndexedDBAvailable()) {
  629. return writeTokenToIndexedDB(app, token).catch(function (e) {
  630. // swallow the error and resolve the promise
  631. logger.warn("Failed to write token to IndexedDB. Error: ".concat(e));
  632. });
  633. }
  634. return Promise.resolve();
  635. }
  636. function readOrCreateDebugTokenFromStorage() {
  637. return __awaiter(this, void 0, void 0, function () {
  638. var existingDebugToken, newToken;
  639. return __generator(this, function (_a) {
  640. switch (_a.label) {
  641. case 0:
  642. existingDebugToken = undefined;
  643. _a.label = 1;
  644. case 1:
  645. _a.trys.push([1, 3, , 4]);
  646. return [4 /*yield*/, readDebugTokenFromIndexedDB()];
  647. case 2:
  648. existingDebugToken = _a.sent();
  649. return [3 /*break*/, 4];
  650. case 3:
  651. _a.sent();
  652. return [3 /*break*/, 4];
  653. case 4:
  654. if (!existingDebugToken) {
  655. newToken = uuidv4();
  656. // We don't need to block on writing to indexeddb
  657. // In case persistence failed, a new debug token will be generated everytime the page is refreshed.
  658. // It renders the debug token useless because you have to manually register(whitelist) the new token in the firebase console again and again.
  659. // If you see this error trying to use debug token, it probably means you are using a browser that doesn't support indexeddb.
  660. // You should switch to a different browser that supports indexeddb
  661. writeDebugTokenToIndexedDB(newToken).catch(function (e) {
  662. return logger.warn("Failed to persist debug token to IndexedDB. Error: ".concat(e));
  663. });
  664. return [2 /*return*/, newToken];
  665. }
  666. else {
  667. return [2 /*return*/, existingDebugToken];
  668. }
  669. }
  670. });
  671. });
  672. }
  673. /**
  674. * @license
  675. * Copyright 2020 Google LLC
  676. *
  677. * Licensed under the Apache License, Version 2.0 (the "License");
  678. * you may not use this file except in compliance with the License.
  679. * You may obtain a copy of the License at
  680. *
  681. * http://www.apache.org/licenses/LICENSE-2.0
  682. *
  683. * Unless required by applicable law or agreed to in writing, software
  684. * distributed under the License is distributed on an "AS IS" BASIS,
  685. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  686. * See the License for the specific language governing permissions and
  687. * limitations under the License.
  688. */
  689. function isDebugMode() {
  690. var debugState = getDebugState();
  691. return debugState.enabled;
  692. }
  693. function getDebugToken() {
  694. return __awaiter(this, void 0, void 0, function () {
  695. var state;
  696. return __generator(this, function (_a) {
  697. state = getDebugState();
  698. if (state.enabled && state.token) {
  699. return [2 /*return*/, state.token.promise];
  700. }
  701. else {
  702. // should not happen!
  703. throw Error("\n Can't get debug token in production mode.\n ");
  704. }
  705. });
  706. });
  707. }
  708. function initializeDebugMode() {
  709. var globals = getGlobal();
  710. var debugState = getDebugState();
  711. // Set to true if this function has been called, whether or not
  712. // it enabled debug mode.
  713. debugState.initialized = true;
  714. if (typeof globals.FIREBASE_APPCHECK_DEBUG_TOKEN !== 'string' &&
  715. globals.FIREBASE_APPCHECK_DEBUG_TOKEN !== true) {
  716. return;
  717. }
  718. debugState.enabled = true;
  719. var deferredToken = new Deferred();
  720. debugState.token = deferredToken;
  721. if (typeof globals.FIREBASE_APPCHECK_DEBUG_TOKEN === 'string') {
  722. deferredToken.resolve(globals.FIREBASE_APPCHECK_DEBUG_TOKEN);
  723. }
  724. else {
  725. deferredToken.resolve(readOrCreateDebugTokenFromStorage());
  726. }
  727. }
  728. /**
  729. * @license
  730. * Copyright 2020 Google LLC
  731. *
  732. * Licensed under the Apache License, Version 2.0 (the "License");
  733. * you may not use this file except in compliance with the License.
  734. * You may obtain a copy of the License at
  735. *
  736. * http://www.apache.org/licenses/LICENSE-2.0
  737. *
  738. * Unless required by applicable law or agreed to in writing, software
  739. * distributed under the License is distributed on an "AS IS" BASIS,
  740. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  741. * See the License for the specific language governing permissions and
  742. * limitations under the License.
  743. */
  744. // Initial hardcoded value agreed upon across platforms for initial launch.
  745. // Format left open for possible dynamic error values and other fields in the future.
  746. var defaultTokenErrorData = { error: 'UNKNOWN_ERROR' };
  747. /**
  748. * Stringify and base64 encode token error data.
  749. *
  750. * @param tokenError Error data, currently hardcoded.
  751. */
  752. function formatDummyToken(tokenErrorData) {
  753. return base64.encodeString(JSON.stringify(tokenErrorData),
  754. /* webSafe= */ false);
  755. }
  756. /**
  757. * This function always resolves.
  758. * The result will contain an error field if there is any error.
  759. * In case there is an error, the token field in the result will be populated with a dummy value
  760. */
  761. function getToken$2(appCheck, forceRefresh) {
  762. if (forceRefresh === void 0) { forceRefresh = false; }
  763. return __awaiter(this, void 0, void 0, function () {
  764. var app, state, token, error, cachedToken, shouldCallListeners, _a, _b, _c, _d, tokenFromDebugExchange, e_1, interopTokenResult;
  765. return __generator(this, function (_e) {
  766. switch (_e.label) {
  767. case 0:
  768. app = appCheck.app;
  769. ensureActivated(app);
  770. state = getStateReference(app);
  771. token = state.token;
  772. error = undefined;
  773. /**
  774. * If an invalid token was found in memory, clear token from
  775. * memory and unset the local variable `token`.
  776. */
  777. if (token && !isValid(token)) {
  778. state.token = undefined;
  779. token = undefined;
  780. }
  781. if (!!token) return [3 /*break*/, 4];
  782. return [4 /*yield*/, state.cachedTokenPromise];
  783. case 1:
  784. cachedToken = _e.sent();
  785. if (!cachedToken) return [3 /*break*/, 4];
  786. if (!isValid(cachedToken)) return [3 /*break*/, 2];
  787. token = cachedToken;
  788. return [3 /*break*/, 4];
  789. case 2:
  790. // If there was an invalid token in the indexedDB cache, clear it.
  791. return [4 /*yield*/, writeTokenToStorage(app, undefined)];
  792. case 3:
  793. // If there was an invalid token in the indexedDB cache, clear it.
  794. _e.sent();
  795. _e.label = 4;
  796. case 4:
  797. // Return the cached token (from either memory or indexedDB) if it's valid
  798. if (!forceRefresh && token && isValid(token)) {
  799. return [2 /*return*/, {
  800. token: token.token
  801. }];
  802. }
  803. shouldCallListeners = false;
  804. if (!isDebugMode()) return [3 /*break*/, 9];
  805. if (!!state.exchangeTokenPromise) return [3 /*break*/, 6];
  806. _a = state;
  807. _b = exchangeToken;
  808. _c = getExchangeDebugTokenRequest;
  809. _d = [app];
  810. return [4 /*yield*/, getDebugToken()];
  811. case 5:
  812. _a.exchangeTokenPromise = _b.apply(void 0, [_c.apply(void 0, _d.concat([_e.sent()])),
  813. appCheck.heartbeatServiceProvider]).finally(function () {
  814. // Clear promise when settled - either resolved or rejected.
  815. state.exchangeTokenPromise = undefined;
  816. });
  817. shouldCallListeners = true;
  818. _e.label = 6;
  819. case 6: return [4 /*yield*/, state.exchangeTokenPromise];
  820. case 7:
  821. tokenFromDebugExchange = _e.sent();
  822. // Write debug token to indexedDB.
  823. return [4 /*yield*/, writeTokenToStorage(app, tokenFromDebugExchange)];
  824. case 8:
  825. // Write debug token to indexedDB.
  826. _e.sent();
  827. // Write debug token to state.
  828. state.token = tokenFromDebugExchange;
  829. return [2 /*return*/, { token: tokenFromDebugExchange.token }];
  830. case 9:
  831. _e.trys.push([9, 11, , 12]);
  832. // Avoid making another call to the exchange endpoint if one is in flight.
  833. if (!state.exchangeTokenPromise) {
  834. // state.provider is populated in initializeAppCheck()
  835. // ensureActivated() at the top of this function checks that
  836. // initializeAppCheck() has been called.
  837. state.exchangeTokenPromise = state.provider.getToken().finally(function () {
  838. // Clear promise when settled - either resolved or rejected.
  839. state.exchangeTokenPromise = undefined;
  840. });
  841. shouldCallListeners = true;
  842. }
  843. return [4 /*yield*/, getStateReference(app).exchangeTokenPromise];
  844. case 10:
  845. token = _e.sent();
  846. return [3 /*break*/, 12];
  847. case 11:
  848. e_1 = _e.sent();
  849. if (e_1.code === "appCheck/".concat("throttled" /* AppCheckError.THROTTLED */)) {
  850. // Warn if throttled, but do not treat it as an error.
  851. logger.warn(e_1.message);
  852. }
  853. else {
  854. // `getToken()` should never throw, but logging error text to console will aid debugging.
  855. logger.error(e_1);
  856. }
  857. // Always save error to be added to dummy token.
  858. error = e_1;
  859. return [3 /*break*/, 12];
  860. case 12:
  861. if (!!token) return [3 /*break*/, 13];
  862. // If token is undefined, there must be an error.
  863. // Return a dummy token along with the error.
  864. interopTokenResult = makeDummyTokenResult(error);
  865. return [3 /*break*/, 16];
  866. case 13:
  867. if (!error) return [3 /*break*/, 14];
  868. if (isValid(token)) {
  869. // It's also possible a valid token exists, but there's also an error.
  870. // (Such as if the token is almost expired, tries to refresh, and
  871. // the exchange request fails.)
  872. // We add a special error property here so that the refresher will
  873. // count this as a failed attempt and use the backoff instead of
  874. // retrying repeatedly with no delay, but any 3P listeners will not
  875. // be hindered in getting the still-valid token.
  876. interopTokenResult = {
  877. token: token.token,
  878. internalError: error
  879. };
  880. }
  881. else {
  882. // No invalid tokens should make it to this step. Memory and cached tokens
  883. // are checked. Other tokens are from fresh exchanges. But just in case.
  884. interopTokenResult = makeDummyTokenResult(error);
  885. }
  886. return [3 /*break*/, 16];
  887. case 14:
  888. interopTokenResult = {
  889. token: token.token
  890. };
  891. // write the new token to the memory state as well as the persistent storage.
  892. // Only do it if we got a valid new token
  893. state.token = token;
  894. return [4 /*yield*/, writeTokenToStorage(app, token)];
  895. case 15:
  896. _e.sent();
  897. _e.label = 16;
  898. case 16:
  899. if (shouldCallListeners) {
  900. notifyTokenListeners(app, interopTokenResult);
  901. }
  902. return [2 /*return*/, interopTokenResult];
  903. }
  904. });
  905. });
  906. }
  907. function addTokenListener(appCheck, type, listener, onError) {
  908. var app = appCheck.app;
  909. var state = getStateReference(app);
  910. var tokenObserver = {
  911. next: listener,
  912. error: onError,
  913. type: type
  914. };
  915. state.tokenObservers = __spreadArray(__spreadArray([], state.tokenObservers, true), [tokenObserver], false);
  916. // Invoke the listener async immediately if there is a valid token
  917. // in memory.
  918. if (state.token && isValid(state.token)) {
  919. var validToken_1 = state.token;
  920. Promise.resolve()
  921. .then(function () {
  922. listener({ token: validToken_1.token });
  923. initTokenRefresher(appCheck);
  924. })
  925. .catch(function () {
  926. /* we don't care about exceptions thrown in listeners */
  927. });
  928. }
  929. /**
  930. * Wait for any cached token promise to resolve before starting the token
  931. * refresher. The refresher checks to see if there is an existing token
  932. * in state and calls the exchange endpoint if not. We should first let the
  933. * IndexedDB check have a chance to populate state if it can.
  934. *
  935. * Listener call isn't needed here because cachedTokenPromise will call any
  936. * listeners that exist when it resolves.
  937. */
  938. // state.cachedTokenPromise is always populated in `activate()`.
  939. void state.cachedTokenPromise.then(function () { return initTokenRefresher(appCheck); });
  940. }
  941. function removeTokenListener(app, listener) {
  942. var state = getStateReference(app);
  943. var newObservers = state.tokenObservers.filter(function (tokenObserver) { return tokenObserver.next !== listener; });
  944. if (newObservers.length === 0 &&
  945. state.tokenRefresher &&
  946. state.tokenRefresher.isRunning()) {
  947. state.tokenRefresher.stop();
  948. }
  949. state.tokenObservers = newObservers;
  950. }
  951. /**
  952. * Logic to create and start refresher as needed.
  953. */
  954. function initTokenRefresher(appCheck) {
  955. var app = appCheck.app;
  956. var state = getStateReference(app);
  957. // Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
  958. // is not true.
  959. var refresher = state.tokenRefresher;
  960. if (!refresher) {
  961. refresher = createTokenRefresher(appCheck);
  962. state.tokenRefresher = refresher;
  963. }
  964. if (!refresher.isRunning() && state.isTokenAutoRefreshEnabled) {
  965. refresher.start();
  966. }
  967. }
  968. function createTokenRefresher(appCheck) {
  969. var _this = this;
  970. var app = appCheck.app;
  971. return new Refresher(
  972. // Keep in mind when this fails for any reason other than the ones
  973. // for which we should retry, it will effectively stop the proactive refresh.
  974. function () { return __awaiter(_this, void 0, void 0, function () {
  975. var state, result;
  976. return __generator(this, function (_a) {
  977. switch (_a.label) {
  978. case 0:
  979. state = getStateReference(app);
  980. if (!!state.token) return [3 /*break*/, 2];
  981. return [4 /*yield*/, getToken$2(appCheck)];
  982. case 1:
  983. result = _a.sent();
  984. return [3 /*break*/, 4];
  985. case 2: return [4 /*yield*/, getToken$2(appCheck, true)];
  986. case 3:
  987. result = _a.sent();
  988. _a.label = 4;
  989. case 4:
  990. /**
  991. * getToken() always resolves. In case the result has an error field defined, it means
  992. * the operation failed, and we should retry.
  993. */
  994. if (result.error) {
  995. throw result.error;
  996. }
  997. /**
  998. * A special `internalError` field reflects that there was an error
  999. * getting a new token from the exchange endpoint, but there's still a
  1000. * previous token that's valid for now and this should be passed to 2P/3P
  1001. * requests for a token. But we want this callback (`this.operation` in
  1002. * `Refresher`) to throw in order to kick off the Refresher's retry
  1003. * backoff. (Setting `hasSucceeded` to false.)
  1004. */
  1005. if (result.internalError) {
  1006. throw result.internalError;
  1007. }
  1008. return [2 /*return*/];
  1009. }
  1010. });
  1011. }); }, function () {
  1012. return true;
  1013. }, function () {
  1014. var state = getStateReference(app);
  1015. if (state.token) {
  1016. // issuedAtTime + (50% * total TTL) + 5 minutes
  1017. var nextRefreshTimeMillis = state.token.issuedAtTimeMillis +
  1018. (state.token.expireTimeMillis - state.token.issuedAtTimeMillis) *
  1019. 0.5 +
  1020. 5 * 60 * 1000;
  1021. // Do not allow refresh time to be past (expireTime - 5 minutes)
  1022. var latestAllowableRefresh = state.token.expireTimeMillis - 5 * 60 * 1000;
  1023. nextRefreshTimeMillis = Math.min(nextRefreshTimeMillis, latestAllowableRefresh);
  1024. return Math.max(0, nextRefreshTimeMillis - Date.now());
  1025. }
  1026. else {
  1027. return 0;
  1028. }
  1029. }, TOKEN_REFRESH_TIME.RETRIAL_MIN_WAIT, TOKEN_REFRESH_TIME.RETRIAL_MAX_WAIT);
  1030. }
  1031. function notifyTokenListeners(app, token) {
  1032. var observers = getStateReference(app).tokenObservers;
  1033. for (var _i = 0, observers_1 = observers; _i < observers_1.length; _i++) {
  1034. var observer = observers_1[_i];
  1035. try {
  1036. if (observer.type === "EXTERNAL" /* ListenerType.EXTERNAL */ && token.error != null) {
  1037. // If this listener was added by a 3P call, send any token error to
  1038. // the supplied error handler. A 3P observer always has an error
  1039. // handler.
  1040. observer.error(token.error);
  1041. }
  1042. else {
  1043. // If the token has no error field, always return the token.
  1044. // If this is a 2P listener, return the token, whether or not it
  1045. // has an error field.
  1046. observer.next(token);
  1047. }
  1048. }
  1049. catch (e) {
  1050. // Errors in the listener function itself are always ignored.
  1051. }
  1052. }
  1053. }
  1054. function isValid(token) {
  1055. return token.expireTimeMillis - Date.now() > 0;
  1056. }
  1057. function makeDummyTokenResult(error) {
  1058. return {
  1059. token: formatDummyToken(defaultTokenErrorData),
  1060. error: error
  1061. };
  1062. }
  1063. /**
  1064. * @license
  1065. * Copyright 2020 Google LLC
  1066. *
  1067. * Licensed under the Apache License, Version 2.0 (the "License");
  1068. * you may not use this file except in compliance with the License.
  1069. * You may obtain a copy of the License at
  1070. *
  1071. * http://www.apache.org/licenses/LICENSE-2.0
  1072. *
  1073. * Unless required by applicable law or agreed to in writing, software
  1074. * distributed under the License is distributed on an "AS IS" BASIS,
  1075. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1076. * See the License for the specific language governing permissions and
  1077. * limitations under the License.
  1078. */
  1079. /**
  1080. * AppCheck Service class.
  1081. */
  1082. var AppCheckService = /** @class */ (function () {
  1083. function AppCheckService(app, heartbeatServiceProvider) {
  1084. this.app = app;
  1085. this.heartbeatServiceProvider = heartbeatServiceProvider;
  1086. }
  1087. AppCheckService.prototype._delete = function () {
  1088. var tokenObservers = getStateReference(this.app).tokenObservers;
  1089. for (var _i = 0, tokenObservers_1 = tokenObservers; _i < tokenObservers_1.length; _i++) {
  1090. var tokenObserver = tokenObservers_1[_i];
  1091. removeTokenListener(this.app, tokenObserver.next);
  1092. }
  1093. return Promise.resolve();
  1094. };
  1095. return AppCheckService;
  1096. }());
  1097. function factory(app, heartbeatServiceProvider) {
  1098. return new AppCheckService(app, heartbeatServiceProvider);
  1099. }
  1100. function internalFactory(appCheck) {
  1101. return {
  1102. getToken: function (forceRefresh) { return getToken$2(appCheck, forceRefresh); },
  1103. addTokenListener: function (listener) {
  1104. return addTokenListener(appCheck, "INTERNAL" /* ListenerType.INTERNAL */, listener);
  1105. },
  1106. removeTokenListener: function (listener) { return removeTokenListener(appCheck.app, listener); }
  1107. };
  1108. }
  1109. var name = "@firebase/app-check";
  1110. var version = "0.6.1";
  1111. /**
  1112. * @license
  1113. * Copyright 2020 Google LLC
  1114. *
  1115. * Licensed under the Apache License, Version 2.0 (the "License");
  1116. * you may not use this file except in compliance with the License.
  1117. * You may obtain a copy of the License at
  1118. *
  1119. * http://www.apache.org/licenses/LICENSE-2.0
  1120. *
  1121. * Unless required by applicable law or agreed to in writing, software
  1122. * distributed under the License is distributed on an "AS IS" BASIS,
  1123. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1124. * See the License for the specific language governing permissions and
  1125. * limitations under the License.
  1126. */
  1127. var RECAPTCHA_URL = 'https://www.google.com/recaptcha/api.js';
  1128. var RECAPTCHA_ENTERPRISE_URL = 'https://www.google.com/recaptcha/enterprise.js';
  1129. function initializeV3(app, siteKey) {
  1130. var initialized = new Deferred();
  1131. var state = getStateReference(app);
  1132. state.reCAPTCHAState = { initialized: initialized };
  1133. var divId = makeDiv(app);
  1134. var grecaptcha = getRecaptcha(false);
  1135. if (!grecaptcha) {
  1136. loadReCAPTCHAV3Script(function () {
  1137. var grecaptcha = getRecaptcha(false);
  1138. if (!grecaptcha) {
  1139. // it shouldn't happen.
  1140. throw new Error('no recaptcha');
  1141. }
  1142. queueWidgetRender(app, siteKey, grecaptcha, divId, initialized);
  1143. });
  1144. }
  1145. else {
  1146. queueWidgetRender(app, siteKey, grecaptcha, divId, initialized);
  1147. }
  1148. return initialized.promise;
  1149. }
  1150. function initializeEnterprise(app, siteKey) {
  1151. var initialized = new Deferred();
  1152. var state = getStateReference(app);
  1153. state.reCAPTCHAState = { initialized: initialized };
  1154. var divId = makeDiv(app);
  1155. var grecaptcha = getRecaptcha(true);
  1156. if (!grecaptcha) {
  1157. loadReCAPTCHAEnterpriseScript(function () {
  1158. var grecaptcha = getRecaptcha(true);
  1159. if (!grecaptcha) {
  1160. // it shouldn't happen.
  1161. throw new Error('no recaptcha');
  1162. }
  1163. queueWidgetRender(app, siteKey, grecaptcha, divId, initialized);
  1164. });
  1165. }
  1166. else {
  1167. queueWidgetRender(app, siteKey, grecaptcha, divId, initialized);
  1168. }
  1169. return initialized.promise;
  1170. }
  1171. /**
  1172. * Add listener to render the widget and resolve the promise when
  1173. * the grecaptcha.ready() event fires.
  1174. */
  1175. function queueWidgetRender(app, siteKey, grecaptcha, container, initialized) {
  1176. grecaptcha.ready(function () {
  1177. // Invisible widgets allow us to set a different siteKey for each widget,
  1178. // so we use them to support multiple apps
  1179. renderInvisibleWidget(app, siteKey, grecaptcha, container);
  1180. initialized.resolve(grecaptcha);
  1181. });
  1182. }
  1183. /**
  1184. * Add invisible div to page.
  1185. */
  1186. function makeDiv(app) {
  1187. var divId = "fire_app_check_".concat(app.name);
  1188. var invisibleDiv = document.createElement('div');
  1189. invisibleDiv.id = divId;
  1190. invisibleDiv.style.display = 'none';
  1191. document.body.appendChild(invisibleDiv);
  1192. return divId;
  1193. }
  1194. function getToken$1(app) {
  1195. return __awaiter(this, void 0, void 0, function () {
  1196. var reCAPTCHAState, recaptcha;
  1197. return __generator(this, function (_a) {
  1198. switch (_a.label) {
  1199. case 0:
  1200. ensureActivated(app);
  1201. reCAPTCHAState = getStateReference(app).reCAPTCHAState;
  1202. return [4 /*yield*/, reCAPTCHAState.initialized.promise];
  1203. case 1:
  1204. recaptcha = _a.sent();
  1205. return [2 /*return*/, new Promise(function (resolve, _reject) {
  1206. // Updated after initialization is complete.
  1207. var reCAPTCHAState = getStateReference(app).reCAPTCHAState;
  1208. recaptcha.ready(function () {
  1209. resolve(
  1210. // widgetId is guaranteed to be available if reCAPTCHAState.initialized.promise resolved.
  1211. recaptcha.execute(reCAPTCHAState.widgetId, {
  1212. action: 'fire_app_check'
  1213. }));
  1214. });
  1215. })];
  1216. }
  1217. });
  1218. });
  1219. }
  1220. /**
  1221. *
  1222. * @param app
  1223. * @param container - Id of a HTML element.
  1224. */
  1225. function renderInvisibleWidget(app, siteKey, grecaptcha, container) {
  1226. var widgetId = grecaptcha.render(container, {
  1227. sitekey: siteKey,
  1228. size: 'invisible'
  1229. });
  1230. var state = getStateReference(app);
  1231. state.reCAPTCHAState = __assign(__assign({}, state.reCAPTCHAState), { // state.reCAPTCHAState is set in the initialize()
  1232. widgetId: widgetId });
  1233. }
  1234. function loadReCAPTCHAV3Script(onload) {
  1235. var script = document.createElement('script');
  1236. script.src = RECAPTCHA_URL;
  1237. script.onload = onload;
  1238. document.head.appendChild(script);
  1239. }
  1240. function loadReCAPTCHAEnterpriseScript(onload) {
  1241. var script = document.createElement('script');
  1242. script.src = RECAPTCHA_ENTERPRISE_URL;
  1243. script.onload = onload;
  1244. document.head.appendChild(script);
  1245. }
  1246. /**
  1247. * @license
  1248. * Copyright 2021 Google LLC
  1249. *
  1250. * Licensed under the Apache License, Version 2.0 (the "License");
  1251. * you may not use this file except in compliance with the License.
  1252. * You may obtain a copy of the License at
  1253. *
  1254. * http://www.apache.org/licenses/LICENSE-2.0
  1255. *
  1256. * Unless required by applicable law or agreed to in writing, software
  1257. * distributed under the License is distributed on an "AS IS" BASIS,
  1258. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1259. * See the License for the specific language governing permissions and
  1260. * limitations under the License.
  1261. */
  1262. /**
  1263. * App Check provider that can obtain a reCAPTCHA V3 token and exchange it
  1264. * for an App Check token.
  1265. *
  1266. * @public
  1267. */
  1268. var ReCaptchaV3Provider = /** @class */ (function () {
  1269. /**
  1270. * Create a ReCaptchaV3Provider instance.
  1271. * @param siteKey - ReCAPTCHA V3 siteKey.
  1272. */
  1273. function ReCaptchaV3Provider(_siteKey) {
  1274. this._siteKey = _siteKey;
  1275. /**
  1276. * Throttle requests on certain error codes to prevent too many retries
  1277. * in a short time.
  1278. */
  1279. this._throttleData = null;
  1280. }
  1281. /**
  1282. * Returns an App Check token.
  1283. * @internal
  1284. */
  1285. ReCaptchaV3Provider.prototype.getToken = function () {
  1286. var _a, _b;
  1287. return __awaiter(this, void 0, void 0, function () {
  1288. var attestedClaimsToken, result, e_1;
  1289. return __generator(this, function (_c) {
  1290. switch (_c.label) {
  1291. case 0:
  1292. throwIfThrottled(this._throttleData);
  1293. return [4 /*yield*/, getToken$1(this._app).catch(function (_e) {
  1294. // reCaptcha.execute() throws null which is not very descriptive.
  1295. throw ERROR_FACTORY.create("recaptcha-error" /* AppCheckError.RECAPTCHA_ERROR */);
  1296. })];
  1297. case 1:
  1298. attestedClaimsToken = _c.sent();
  1299. _c.label = 2;
  1300. case 2:
  1301. _c.trys.push([2, 4, , 5]);
  1302. return [4 /*yield*/, exchangeToken(getExchangeRecaptchaV3TokenRequest(this._app, attestedClaimsToken), this._heartbeatServiceProvider)];
  1303. case 3:
  1304. result = _c.sent();
  1305. return [3 /*break*/, 5];
  1306. case 4:
  1307. e_1 = _c.sent();
  1308. if ((_a = e_1.code) === null || _a === void 0 ? void 0 : _a.includes("fetch-status-error" /* AppCheckError.FETCH_STATUS_ERROR */)) {
  1309. this._throttleData = setBackoff(Number((_b = e_1.customData) === null || _b === void 0 ? void 0 : _b.httpStatus), this._throttleData);
  1310. throw ERROR_FACTORY.create("throttled" /* AppCheckError.THROTTLED */, {
  1311. time: getDurationString(this._throttleData.allowRequestsAfter - Date.now()),
  1312. httpStatus: this._throttleData.httpStatus
  1313. });
  1314. }
  1315. else {
  1316. throw e_1;
  1317. }
  1318. case 5:
  1319. // If successful, clear throttle data.
  1320. this._throttleData = null;
  1321. return [2 /*return*/, result];
  1322. }
  1323. });
  1324. });
  1325. };
  1326. /**
  1327. * @internal
  1328. */
  1329. ReCaptchaV3Provider.prototype.initialize = function (app) {
  1330. this._app = app;
  1331. this._heartbeatServiceProvider = _getProvider(app, 'heartbeat');
  1332. initializeV3(app, this._siteKey).catch(function () {
  1333. /* we don't care about the initialization result */
  1334. });
  1335. };
  1336. /**
  1337. * @internal
  1338. */
  1339. ReCaptchaV3Provider.prototype.isEqual = function (otherProvider) {
  1340. if (otherProvider instanceof ReCaptchaV3Provider) {
  1341. return this._siteKey === otherProvider._siteKey;
  1342. }
  1343. else {
  1344. return false;
  1345. }
  1346. };
  1347. return ReCaptchaV3Provider;
  1348. }());
  1349. /**
  1350. * App Check provider that can obtain a reCAPTCHA Enterprise token and exchange it
  1351. * for an App Check token.
  1352. *
  1353. * @public
  1354. */
  1355. var ReCaptchaEnterpriseProvider = /** @class */ (function () {
  1356. /**
  1357. * Create a ReCaptchaEnterpriseProvider instance.
  1358. * @param siteKey - reCAPTCHA Enterprise score-based site key.
  1359. */
  1360. function ReCaptchaEnterpriseProvider(_siteKey) {
  1361. this._siteKey = _siteKey;
  1362. /**
  1363. * Throttle requests on certain error codes to prevent too many retries
  1364. * in a short time.
  1365. */
  1366. this._throttleData = null;
  1367. }
  1368. /**
  1369. * Returns an App Check token.
  1370. * @internal
  1371. */
  1372. ReCaptchaEnterpriseProvider.prototype.getToken = function () {
  1373. var _a, _b;
  1374. return __awaiter(this, void 0, void 0, function () {
  1375. var attestedClaimsToken, result, e_2;
  1376. return __generator(this, function (_c) {
  1377. switch (_c.label) {
  1378. case 0:
  1379. throwIfThrottled(this._throttleData);
  1380. return [4 /*yield*/, getToken$1(this._app).catch(function (_e) {
  1381. // reCaptcha.execute() throws null which is not very descriptive.
  1382. throw ERROR_FACTORY.create("recaptcha-error" /* AppCheckError.RECAPTCHA_ERROR */);
  1383. })];
  1384. case 1:
  1385. attestedClaimsToken = _c.sent();
  1386. _c.label = 2;
  1387. case 2:
  1388. _c.trys.push([2, 4, , 5]);
  1389. return [4 /*yield*/, exchangeToken(getExchangeRecaptchaEnterpriseTokenRequest(this._app, attestedClaimsToken), this._heartbeatServiceProvider)];
  1390. case 3:
  1391. result = _c.sent();
  1392. return [3 /*break*/, 5];
  1393. case 4:
  1394. e_2 = _c.sent();
  1395. if ((_a = e_2.code) === null || _a === void 0 ? void 0 : _a.includes("fetch-status-error" /* AppCheckError.FETCH_STATUS_ERROR */)) {
  1396. this._throttleData = setBackoff(Number((_b = e_2.customData) === null || _b === void 0 ? void 0 : _b.httpStatus), this._throttleData);
  1397. throw ERROR_FACTORY.create("throttled" /* AppCheckError.THROTTLED */, {
  1398. time: getDurationString(this._throttleData.allowRequestsAfter - Date.now()),
  1399. httpStatus: this._throttleData.httpStatus
  1400. });
  1401. }
  1402. else {
  1403. throw e_2;
  1404. }
  1405. case 5:
  1406. // If successful, clear throttle data.
  1407. this._throttleData = null;
  1408. return [2 /*return*/, result];
  1409. }
  1410. });
  1411. });
  1412. };
  1413. /**
  1414. * @internal
  1415. */
  1416. ReCaptchaEnterpriseProvider.prototype.initialize = function (app) {
  1417. this._app = app;
  1418. this._heartbeatServiceProvider = _getProvider(app, 'heartbeat');
  1419. initializeEnterprise(app, this._siteKey).catch(function () {
  1420. /* we don't care about the initialization result */
  1421. });
  1422. };
  1423. /**
  1424. * @internal
  1425. */
  1426. ReCaptchaEnterpriseProvider.prototype.isEqual = function (otherProvider) {
  1427. if (otherProvider instanceof ReCaptchaEnterpriseProvider) {
  1428. return this._siteKey === otherProvider._siteKey;
  1429. }
  1430. else {
  1431. return false;
  1432. }
  1433. };
  1434. return ReCaptchaEnterpriseProvider;
  1435. }());
  1436. /**
  1437. * Custom provider class.
  1438. * @public
  1439. */
  1440. var CustomProvider = /** @class */ (function () {
  1441. function CustomProvider(_customProviderOptions) {
  1442. this._customProviderOptions = _customProviderOptions;
  1443. }
  1444. /**
  1445. * @internal
  1446. */
  1447. CustomProvider.prototype.getToken = function () {
  1448. return __awaiter(this, void 0, void 0, function () {
  1449. var customToken, issuedAtTimeSeconds, issuedAtTimeMillis;
  1450. return __generator(this, function (_a) {
  1451. switch (_a.label) {
  1452. case 0: return [4 /*yield*/, this._customProviderOptions.getToken()];
  1453. case 1:
  1454. customToken = _a.sent();
  1455. issuedAtTimeSeconds = issuedAtTime(customToken.token);
  1456. issuedAtTimeMillis = issuedAtTimeSeconds !== null &&
  1457. issuedAtTimeSeconds < Date.now() &&
  1458. issuedAtTimeSeconds > 0
  1459. ? issuedAtTimeSeconds * 1000
  1460. : Date.now();
  1461. return [2 /*return*/, __assign(__assign({}, customToken), { issuedAtTimeMillis: issuedAtTimeMillis })];
  1462. }
  1463. });
  1464. });
  1465. };
  1466. /**
  1467. * @internal
  1468. */
  1469. CustomProvider.prototype.initialize = function (app) {
  1470. this._app = app;
  1471. };
  1472. /**
  1473. * @internal
  1474. */
  1475. CustomProvider.prototype.isEqual = function (otherProvider) {
  1476. if (otherProvider instanceof CustomProvider) {
  1477. return (this._customProviderOptions.getToken.toString() ===
  1478. otherProvider._customProviderOptions.getToken.toString());
  1479. }
  1480. else {
  1481. return false;
  1482. }
  1483. };
  1484. return CustomProvider;
  1485. }());
  1486. /**
  1487. * Set throttle data to block requests until after a certain time
  1488. * depending on the failed request's status code.
  1489. * @param httpStatus - Status code of failed request.
  1490. * @param throttleData - `ThrottleData` object containing previous throttle
  1491. * data state.
  1492. * @returns Data about current throttle state and expiration time.
  1493. */
  1494. function setBackoff(httpStatus, throttleData) {
  1495. /**
  1496. * Block retries for 1 day for the following error codes:
  1497. *
  1498. * 404: Likely malformed URL.
  1499. *
  1500. * 403:
  1501. * - Attestation failed
  1502. * - Wrong API key
  1503. * - Project deleted
  1504. */
  1505. if (httpStatus === 404 || httpStatus === 403) {
  1506. return {
  1507. backoffCount: 1,
  1508. allowRequestsAfter: Date.now() + ONE_DAY,
  1509. httpStatus: httpStatus
  1510. };
  1511. }
  1512. else {
  1513. /**
  1514. * For all other error codes, the time when it is ok to retry again
  1515. * is based on exponential backoff.
  1516. */
  1517. var backoffCount = throttleData ? throttleData.backoffCount : 0;
  1518. var backoffMillis = calculateBackoffMillis(backoffCount, 1000, 2);
  1519. return {
  1520. backoffCount: backoffCount + 1,
  1521. allowRequestsAfter: Date.now() + backoffMillis,
  1522. httpStatus: httpStatus
  1523. };
  1524. }
  1525. }
  1526. function throwIfThrottled(throttleData) {
  1527. if (throttleData) {
  1528. if (Date.now() - throttleData.allowRequestsAfter <= 0) {
  1529. // If before, throw.
  1530. throw ERROR_FACTORY.create("throttled" /* AppCheckError.THROTTLED */, {
  1531. time: getDurationString(throttleData.allowRequestsAfter - Date.now()),
  1532. httpStatus: throttleData.httpStatus
  1533. });
  1534. }
  1535. }
  1536. }
  1537. /**
  1538. * @license
  1539. * Copyright 2020 Google LLC
  1540. *
  1541. * Licensed under the Apache License, Version 2.0 (the "License");
  1542. * you may not use this file except in compliance with the License.
  1543. * You may obtain a copy of the License at
  1544. *
  1545. * http://www.apache.org/licenses/LICENSE-2.0
  1546. *
  1547. * Unless required by applicable law or agreed to in writing, software
  1548. * distributed under the License is distributed on an "AS IS" BASIS,
  1549. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1550. * See the License for the specific language governing permissions and
  1551. * limitations under the License.
  1552. */
  1553. /**
  1554. * Activate App Check for the given app. Can be called only once per app.
  1555. * @param app - the {@link @firebase/app#FirebaseApp} to activate App Check for
  1556. * @param options - App Check initialization options
  1557. * @public
  1558. */
  1559. function initializeAppCheck(app, options) {
  1560. if (app === void 0) { app = getApp(); }
  1561. app = getModularInstance(app);
  1562. var provider = _getProvider(app, 'app-check');
  1563. // Ensure initializeDebugMode() is only called once.
  1564. if (!getDebugState().initialized) {
  1565. initializeDebugMode();
  1566. }
  1567. // Log a message containing the debug token when `initializeAppCheck()`
  1568. // is called in debug mode.
  1569. if (isDebugMode()) {
  1570. // Do not block initialization to get the token for the message.
  1571. void getDebugToken().then(function (token) {
  1572. // Not using logger because I don't think we ever want this accidentally hidden.
  1573. return console.log("App Check debug token: ".concat(token, ". You will need to add it to your app's App Check settings in the Firebase console for it to work."));
  1574. });
  1575. }
  1576. if (provider.isInitialized()) {
  1577. var existingInstance = provider.getImmediate();
  1578. var initialOptions = provider.getOptions();
  1579. if (initialOptions.isTokenAutoRefreshEnabled ===
  1580. options.isTokenAutoRefreshEnabled &&
  1581. initialOptions.provider.isEqual(options.provider)) {
  1582. return existingInstance;
  1583. }
  1584. else {
  1585. throw ERROR_FACTORY.create("already-initialized" /* AppCheckError.ALREADY_INITIALIZED */, {
  1586. appName: app.name
  1587. });
  1588. }
  1589. }
  1590. var appCheck = provider.initialize({ options: options });
  1591. _activate(app, options.provider, options.isTokenAutoRefreshEnabled);
  1592. // If isTokenAutoRefreshEnabled is false, do not send any requests to the
  1593. // exchange endpoint without an explicit call from the user either directly
  1594. // or through another Firebase library (storage, functions, etc.)
  1595. if (getStateReference(app).isTokenAutoRefreshEnabled) {
  1596. // Adding a listener will start the refresher and fetch a token if needed.
  1597. // This gets a token ready and prevents a delay when an internal library
  1598. // requests the token.
  1599. // Listener function does not need to do anything, its base functionality
  1600. // of calling getToken() already fetches token and writes it to memory/storage.
  1601. addTokenListener(appCheck, "INTERNAL" /* ListenerType.INTERNAL */, function () { });
  1602. }
  1603. return appCheck;
  1604. }
  1605. /**
  1606. * Activate App Check
  1607. * @param app - Firebase app to activate App Check for.
  1608. * @param provider - reCAPTCHA v3 provider or
  1609. * custom token provider.
  1610. * @param isTokenAutoRefreshEnabled - If true, the SDK automatically
  1611. * refreshes App Check tokens as needed. If undefined, defaults to the
  1612. * value of `app.automaticDataCollectionEnabled`, which defaults to
  1613. * false and can be set in the app config.
  1614. */
  1615. function _activate(app, provider, isTokenAutoRefreshEnabled) {
  1616. // Create an entry in the APP_CHECK_STATES map. Further changes should
  1617. // directly mutate this object.
  1618. var state = setInitialState(app, __assign({}, DEFAULT_STATE));
  1619. state.activated = true;
  1620. state.provider = provider; // Read cached token from storage if it exists and store it in memory.
  1621. state.cachedTokenPromise = readTokenFromStorage(app).then(function (cachedToken) {
  1622. if (cachedToken && isValid(cachedToken)) {
  1623. state.token = cachedToken;
  1624. // notify all listeners with the cached token
  1625. notifyTokenListeners(app, { token: cachedToken.token });
  1626. }
  1627. return cachedToken;
  1628. });
  1629. // Use value of global `automaticDataCollectionEnabled` (which
  1630. // itself defaults to false if not specified in config) if
  1631. // `isTokenAutoRefreshEnabled` param was not provided by user.
  1632. state.isTokenAutoRefreshEnabled =
  1633. isTokenAutoRefreshEnabled === undefined
  1634. ? app.automaticDataCollectionEnabled
  1635. : isTokenAutoRefreshEnabled;
  1636. state.provider.initialize(app);
  1637. }
  1638. /**
  1639. * Set whether App Check will automatically refresh tokens as needed.
  1640. *
  1641. * @param appCheckInstance - The App Check service instance.
  1642. * @param isTokenAutoRefreshEnabled - If true, the SDK automatically
  1643. * refreshes App Check tokens as needed. This overrides any value set
  1644. * during `initializeAppCheck()`.
  1645. * @public
  1646. */
  1647. function setTokenAutoRefreshEnabled(appCheckInstance, isTokenAutoRefreshEnabled) {
  1648. var app = appCheckInstance.app;
  1649. var state = getStateReference(app);
  1650. // This will exist if any product libraries have called
  1651. // `addTokenListener()`
  1652. if (state.tokenRefresher) {
  1653. if (isTokenAutoRefreshEnabled === true) {
  1654. state.tokenRefresher.start();
  1655. }
  1656. else {
  1657. state.tokenRefresher.stop();
  1658. }
  1659. }
  1660. state.isTokenAutoRefreshEnabled = isTokenAutoRefreshEnabled;
  1661. }
  1662. /**
  1663. * Get the current App Check token. Attaches to the most recent
  1664. * in-flight request if one is present. Returns null if no token
  1665. * is present and no token requests are in-flight.
  1666. *
  1667. * @param appCheckInstance - The App Check service instance.
  1668. * @param forceRefresh - If true, will always try to fetch a fresh token.
  1669. * If false, will use a cached token if found in storage.
  1670. * @public
  1671. */
  1672. function getToken(appCheckInstance, forceRefresh) {
  1673. return __awaiter(this, void 0, void 0, function () {
  1674. var result;
  1675. return __generator(this, function (_a) {
  1676. switch (_a.label) {
  1677. case 0: return [4 /*yield*/, getToken$2(appCheckInstance, forceRefresh)];
  1678. case 1:
  1679. result = _a.sent();
  1680. if (result.error) {
  1681. throw result.error;
  1682. }
  1683. return [2 /*return*/, { token: result.token }];
  1684. }
  1685. });
  1686. });
  1687. }
  1688. /**
  1689. * Wraps `addTokenListener`/`removeTokenListener` methods in an `Observer`
  1690. * pattern for public use.
  1691. */
  1692. function onTokenChanged(appCheckInstance, onNextOrObserver, onError,
  1693. /**
  1694. * NOTE: Although an `onCompletion` callback can be provided, it will
  1695. * never be called because the token stream is never-ending.
  1696. * It is added only for API consistency with the observer pattern, which
  1697. * we follow in JS APIs.
  1698. */
  1699. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  1700. onCompletion) {
  1701. var nextFn = function () { };
  1702. var errorFn = function () { };
  1703. if (onNextOrObserver.next != null) {
  1704. nextFn = onNextOrObserver.next.bind(onNextOrObserver);
  1705. }
  1706. else {
  1707. nextFn = onNextOrObserver;
  1708. }
  1709. if (onNextOrObserver.error != null) {
  1710. errorFn = onNextOrObserver.error.bind(onNextOrObserver);
  1711. }
  1712. else if (onError) {
  1713. errorFn = onError;
  1714. }
  1715. addTokenListener(appCheckInstance, "EXTERNAL" /* ListenerType.EXTERNAL */, nextFn, errorFn);
  1716. return function () { return removeTokenListener(appCheckInstance.app, nextFn); };
  1717. }
  1718. /**
  1719. * Firebase App Check
  1720. *
  1721. * @packageDocumentation
  1722. */
  1723. var APP_CHECK_NAME = 'app-check';
  1724. var APP_CHECK_NAME_INTERNAL = 'app-check-internal';
  1725. function registerAppCheck() {
  1726. // The public interface
  1727. _registerComponent(new Component(APP_CHECK_NAME, function (container) {
  1728. // getImmediate for FirebaseApp will always succeed
  1729. var app = container.getProvider('app').getImmediate();
  1730. var heartbeatServiceProvider = container.getProvider('heartbeat');
  1731. return factory(app, heartbeatServiceProvider);
  1732. }, "PUBLIC" /* ComponentType.PUBLIC */)
  1733. .setInstantiationMode("EXPLICIT" /* InstantiationMode.EXPLICIT */)
  1734. /**
  1735. * Initialize app-check-internal after app-check is initialized to make AppCheck available to
  1736. * other Firebase SDKs
  1737. */
  1738. .setInstanceCreatedCallback(function (container, _identifier, _appcheckService) {
  1739. container.getProvider(APP_CHECK_NAME_INTERNAL).initialize();
  1740. }));
  1741. // The internal interface used by other Firebase products
  1742. _registerComponent(new Component(APP_CHECK_NAME_INTERNAL, function (container) {
  1743. var appCheck = container.getProvider('app-check').getImmediate();
  1744. return internalFactory(appCheck);
  1745. }, "PUBLIC" /* ComponentType.PUBLIC */).setInstantiationMode("EXPLICIT" /* InstantiationMode.EXPLICIT */));
  1746. registerVersion(name, version);
  1747. }
  1748. registerAppCheck();
  1749. export { CustomProvider, ReCaptchaEnterpriseProvider, ReCaptchaV3Provider, getToken, initializeAppCheck, onTokenChanged, setTokenAutoRefreshEnabled };
  1750. //# sourceMappingURL=index.esm.js.map