firebase 9.4.1 → 9.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/app/dist/index.cjs.js +1 -1
- package/app/dist/index.esm.js +1 -1
- package/app/dist/index.mjs +1 -1
- package/compat/app/dist/index.cjs.js +1 -1
- package/compat/app/dist/index.esm.js +1 -1
- package/compat/app/dist/index.mjs +1 -1
- package/compat/dist/index.esm.js +2 -2
- package/compat/dist/index.node.cjs +2 -2
- package/compat/dist/index.rn.cjs.js +2 -2
- package/firebase-analytics.js +3 -3
- package/firebase-app-check-compat.js +2 -2
- package/firebase-app-check-compat.js.map +1 -1
- package/firebase-app-check.js +285 -50
- package/firebase-app-check.js.map +1 -1
- package/firebase-app-compat.js +2 -2
- package/firebase-app-compat.js.map +1 -1
- package/firebase-app.js +9 -9
- package/firebase-auth.js +2 -2
- package/firebase-compat.js +4 -4
- package/firebase-compat.js.map +1 -1
- package/firebase-database.js +4 -4
- package/firebase-firestore-compat.js +1 -1
- package/firebase-firestore-compat.js.map +1 -1
- package/firebase-firestore-lite.js +7 -7
- package/firebase-firestore-lite.js.map +1 -1
- package/firebase-firestore.js +12 -12
- package/firebase-firestore.js.map +1 -1
- package/firebase-functions.js +2 -2
- package/firebase-messaging-sw.js +1 -1
- package/firebase-messaging.js +1 -1
- package/firebase-performance-standalone-compat.es2017.js +5 -5
- package/firebase-performance-standalone-compat.es2017.js.map +1 -1
- package/firebase-performance-standalone-compat.js +1 -1
- package/firebase-performance-standalone-compat.js.map +1 -1
- package/firebase-performance.js +3 -3
- package/firebase-remote-config.js +2 -2
- package/firebase-storage-compat.js +1 -1
- package/firebase-storage-compat.js.map +1 -1
- package/firebase-storage.js +177 -24
- package/firebase-storage.js.map +1 -1
- package/package.json +9 -9
package/firebase-app-check.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _getProvider, getApp, _registerComponent, registerVersion } from 'https://www.gstatic.com/firebasejs/9.
|
|
1
|
+
import { _getProvider, getApp, _registerComponent, registerVersion } from 'https://www.gstatic.com/firebasejs/9.5.0/firebase-app.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @license
|
|
@@ -571,6 +571,71 @@ const issuedAtTime = function (token) {
|
|
|
571
571
|
return null;
|
|
572
572
|
};
|
|
573
573
|
|
|
574
|
+
/**
|
|
575
|
+
* @license
|
|
576
|
+
* Copyright 2019 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
|
+
/**
|
|
591
|
+
* The amount of milliseconds to exponentially increase.
|
|
592
|
+
*/
|
|
593
|
+
const DEFAULT_INTERVAL_MILLIS = 1000;
|
|
594
|
+
/**
|
|
595
|
+
* The factor to backoff by.
|
|
596
|
+
* Should be a number greater than 1.
|
|
597
|
+
*/
|
|
598
|
+
const DEFAULT_BACKOFF_FACTOR = 2;
|
|
599
|
+
/**
|
|
600
|
+
* The maximum milliseconds to increase to.
|
|
601
|
+
*
|
|
602
|
+
* <p>Visible for testing
|
|
603
|
+
*/
|
|
604
|
+
const MAX_VALUE_MILLIS = 4 * 60 * 60 * 1000; // Four hours, like iOS and Android.
|
|
605
|
+
/**
|
|
606
|
+
* The percentage of backoff time to randomize by.
|
|
607
|
+
* See
|
|
608
|
+
* http://go/safe-client-behavior#step-1-determine-the-appropriate-retry-interval-to-handle-spike-traffic
|
|
609
|
+
* for context.
|
|
610
|
+
*
|
|
611
|
+
* <p>Visible for testing
|
|
612
|
+
*/
|
|
613
|
+
const RANDOM_FACTOR = 0.5;
|
|
614
|
+
/**
|
|
615
|
+
* Based on the backoff method from
|
|
616
|
+
* https://github.com/google/closure-library/blob/master/closure/goog/math/exponentialbackoff.js.
|
|
617
|
+
* Extracted here so we don't need to pass metadata and a stateful ExponentialBackoff object around.
|
|
618
|
+
*/
|
|
619
|
+
function calculateBackoffMillis(backoffCount, intervalMillis = DEFAULT_INTERVAL_MILLIS, backoffFactor = DEFAULT_BACKOFF_FACTOR) {
|
|
620
|
+
// Calculates an exponentially increasing value.
|
|
621
|
+
// Deviation: calculates value from count and a constant interval, so we only need to save value
|
|
622
|
+
// and count to restore state.
|
|
623
|
+
const currBaseValue = intervalMillis * Math.pow(backoffFactor, backoffCount);
|
|
624
|
+
// A random "fuzz" to avoid waves of retries.
|
|
625
|
+
// Deviation: randomFactor is required.
|
|
626
|
+
const randomWait = Math.round(
|
|
627
|
+
// A fraction of the backoff value to add/subtract.
|
|
628
|
+
// Deviation: changes multiplication order to improve readability.
|
|
629
|
+
RANDOM_FACTOR *
|
|
630
|
+
currBaseValue *
|
|
631
|
+
// A random float (rounded to int by Math.round above) in the range [-1, 1]. Determines
|
|
632
|
+
// if we add or subtract.
|
|
633
|
+
(Math.random() - 0.5) *
|
|
634
|
+
2);
|
|
635
|
+
// Limits backoff to max to avoid effectively permanent backoff.
|
|
636
|
+
return Math.min(MAX_VALUE_MILLIS, currBaseValue + randomWait);
|
|
637
|
+
}
|
|
638
|
+
|
|
574
639
|
/**
|
|
575
640
|
* @license
|
|
576
641
|
* Copyright 2021 Google LLC
|
|
@@ -862,7 +927,11 @@ const TOKEN_REFRESH_TIME = {
|
|
|
862
927
|
* This is the maximum retrial wait, currently 16 minutes.
|
|
863
928
|
*/
|
|
864
929
|
RETRIAL_MAX_WAIT: 16 * 60 * 1000
|
|
865
|
-
};
|
|
930
|
+
};
|
|
931
|
+
/**
|
|
932
|
+
* One day in millis, for certain error code backoffs.
|
|
933
|
+
*/
|
|
934
|
+
const ONE_DAY = 24 * 60 * 60 * 1000;
|
|
866
935
|
|
|
867
936
|
/**
|
|
868
937
|
* @license
|
|
@@ -1003,7 +1072,8 @@ const ERRORS = {
|
|
|
1003
1072
|
["storage-open" /* STORAGE_OPEN */]: 'Error thrown when opening storage. Original error: {$originalErrorMessage}.',
|
|
1004
1073
|
["storage-get" /* STORAGE_GET */]: 'Error thrown when reading from storage. Original error: {$originalErrorMessage}.',
|
|
1005
1074
|
["storage-set" /* STORAGE_WRITE */]: 'Error thrown when writing to storage. Original error: {$originalErrorMessage}.',
|
|
1006
|
-
["recaptcha-error" /* RECAPTCHA_ERROR */]: 'ReCAPTCHA error.'
|
|
1075
|
+
["recaptcha-error" /* RECAPTCHA_ERROR */]: 'ReCAPTCHA error.',
|
|
1076
|
+
["throttled" /* THROTTLED */]: `Requests throttled due to {$httpStatus} error. Attempts allowed again after {$time}`
|
|
1007
1077
|
};
|
|
1008
1078
|
const ERROR_FACTORY = new ErrorFactory('appCheck', 'AppCheck', ERRORS);
|
|
1009
1079
|
|
|
@@ -1045,6 +1115,28 @@ function uuidv4() {
|
|
|
1045
1115
|
const r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
1046
1116
|
return v.toString(16);
|
|
1047
1117
|
});
|
|
1118
|
+
}
|
|
1119
|
+
function getDurationString(durationInMillis) {
|
|
1120
|
+
const totalSeconds = Math.round(durationInMillis / 1000);
|
|
1121
|
+
const days = Math.floor(totalSeconds / (3600 * 24));
|
|
1122
|
+
const hours = Math.floor((totalSeconds - days * 3600 * 24) / 3600);
|
|
1123
|
+
const minutes = Math.floor((totalSeconds - days * 3600 * 24 - hours * 3600) / 60);
|
|
1124
|
+
const seconds = totalSeconds - days * 3600 * 24 - hours * 3600 - minutes * 60;
|
|
1125
|
+
let result = '';
|
|
1126
|
+
if (days) {
|
|
1127
|
+
result += pad(days) + 'd:';
|
|
1128
|
+
}
|
|
1129
|
+
if (hours) {
|
|
1130
|
+
result += pad(hours) + 'h:';
|
|
1131
|
+
}
|
|
1132
|
+
result += pad(minutes) + 'm:' + pad(seconds) + 's';
|
|
1133
|
+
return result;
|
|
1134
|
+
}
|
|
1135
|
+
function pad(value) {
|
|
1136
|
+
if (value === 0) {
|
|
1137
|
+
return '00';
|
|
1138
|
+
}
|
|
1139
|
+
return value >= 10 ? value.toString() : '0' + value;
|
|
1048
1140
|
}
|
|
1049
1141
|
|
|
1050
1142
|
/**
|
|
@@ -1284,7 +1376,7 @@ function computeKey(app) {
|
|
|
1284
1376
|
* See the License for the specific language governing permissions and
|
|
1285
1377
|
* limitations under the License.
|
|
1286
1378
|
*/
|
|
1287
|
-
const logger = new Logger('https://www.gstatic.com/firebasejs/9.
|
|
1379
|
+
const logger = new Logger('https://www.gstatic.com/firebasejs/9.5.0/firebase-app.js-check');
|
|
1288
1380
|
|
|
1289
1381
|
/**
|
|
1290
1382
|
* @license
|
|
@@ -1462,9 +1554,6 @@ async function getToken$2(appCheck, forceRefresh = false) {
|
|
|
1462
1554
|
const cachedToken = await state.cachedTokenPromise;
|
|
1463
1555
|
if (cachedToken && isValid(cachedToken)) {
|
|
1464
1556
|
token = cachedToken;
|
|
1465
|
-
setState(app, Object.assign(Object.assign({}, state), { token }));
|
|
1466
|
-
// notify all listeners with the cached token
|
|
1467
|
-
notifyTokenListeners(app, { token: token.token });
|
|
1468
1557
|
}
|
|
1469
1558
|
}
|
|
1470
1559
|
// Return the cached token (from either memory or indexedDB) if it's valid
|
|
@@ -1473,13 +1562,25 @@ async function getToken$2(appCheck, forceRefresh = false) {
|
|
|
1473
1562
|
token: token.token
|
|
1474
1563
|
};
|
|
1475
1564
|
}
|
|
1565
|
+
// Only set to true if this `getToken()` call is making the actual
|
|
1566
|
+
// REST call to the exchange endpoint, versus waiting for an already
|
|
1567
|
+
// in-flight call (see debug and regular exchange endpoint paths below)
|
|
1568
|
+
let shouldCallListeners = false;
|
|
1476
1569
|
/**
|
|
1477
1570
|
* DEBUG MODE
|
|
1478
1571
|
* If debug mode is set, and there is no cached token, fetch a new App
|
|
1479
1572
|
* Check token using the debug token, and return it directly.
|
|
1480
1573
|
*/
|
|
1481
1574
|
if (isDebugMode()) {
|
|
1482
|
-
|
|
1575
|
+
// Avoid making another call to the exchange endpoint if one is in flight.
|
|
1576
|
+
if (!state.exchangeTokenPromise) {
|
|
1577
|
+
state.exchangeTokenPromise = exchangeToken(getExchangeDebugTokenRequest(app, await getDebugToken()), appCheck.platformLoggerProvider).then(token => {
|
|
1578
|
+
state.exchangeTokenPromise = undefined;
|
|
1579
|
+
return token;
|
|
1580
|
+
});
|
|
1581
|
+
shouldCallListeners = true;
|
|
1582
|
+
}
|
|
1583
|
+
const tokenFromDebugExchange = await state.exchangeTokenPromise;
|
|
1483
1584
|
// Write debug token to indexedDB.
|
|
1484
1585
|
await writeTokenToStorage(app, tokenFromDebugExchange);
|
|
1485
1586
|
// Write debug token to state.
|
|
@@ -1490,14 +1591,29 @@ async function getToken$2(appCheck, forceRefresh = false) {
|
|
|
1490
1591
|
* request a new token
|
|
1491
1592
|
*/
|
|
1492
1593
|
try {
|
|
1493
|
-
//
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1594
|
+
// Avoid making another call to the exchange endpoint if one is in flight.
|
|
1595
|
+
if (!state.exchangeTokenPromise) {
|
|
1596
|
+
// state.provider is populated in initializeAppCheck()
|
|
1597
|
+
// ensureActivated() at the top of this function checks that
|
|
1598
|
+
// initializeAppCheck() has been called.
|
|
1599
|
+
state.exchangeTokenPromise = state.provider.getToken().then(token => {
|
|
1600
|
+
state.exchangeTokenPromise = undefined;
|
|
1601
|
+
return token;
|
|
1602
|
+
});
|
|
1603
|
+
shouldCallListeners = true;
|
|
1604
|
+
}
|
|
1605
|
+
token = await state.exchangeTokenPromise;
|
|
1497
1606
|
}
|
|
1498
1607
|
catch (e) {
|
|
1499
|
-
|
|
1500
|
-
|
|
1608
|
+
if (e.code === `appCheck/${"throttled" /* THROTTLED */}`) {
|
|
1609
|
+
// Warn if throttled, but do not treat it as an error.
|
|
1610
|
+
logger.warn(e.message);
|
|
1611
|
+
}
|
|
1612
|
+
else {
|
|
1613
|
+
// `getToken()` should never throw, but logging error text to console will aid debugging.
|
|
1614
|
+
logger.error(e);
|
|
1615
|
+
}
|
|
1616
|
+
// Always save error to be added to dummy token.
|
|
1501
1617
|
error = e;
|
|
1502
1618
|
}
|
|
1503
1619
|
let interopTokenResult;
|
|
@@ -1515,7 +1631,9 @@ async function getToken$2(appCheck, forceRefresh = false) {
|
|
|
1515
1631
|
setState(app, Object.assign(Object.assign({}, state), { token }));
|
|
1516
1632
|
await writeTokenToStorage(app, token);
|
|
1517
1633
|
}
|
|
1518
|
-
|
|
1634
|
+
if (shouldCallListeners) {
|
|
1635
|
+
notifyTokenListeners(app, interopTokenResult);
|
|
1636
|
+
}
|
|
1519
1637
|
return interopTokenResult;
|
|
1520
1638
|
}
|
|
1521
1639
|
function addTokenListener(appCheck, type, listener, onError) {
|
|
@@ -1526,44 +1644,31 @@ function addTokenListener(appCheck, type, listener, onError) {
|
|
|
1526
1644
|
error: onError,
|
|
1527
1645
|
type
|
|
1528
1646
|
};
|
|
1529
|
-
|
|
1530
|
-
/**
|
|
1531
|
-
* Invoke the listener with the valid token, then start the token refresher
|
|
1532
|
-
*/
|
|
1533
|
-
if (!newState.tokenRefresher) {
|
|
1534
|
-
const tokenRefresher = createTokenRefresher(appCheck);
|
|
1535
|
-
newState.tokenRefresher = tokenRefresher;
|
|
1536
|
-
}
|
|
1537
|
-
// Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
|
|
1538
|
-
// is not true.
|
|
1539
|
-
if (!newState.tokenRefresher.isRunning() && state.isTokenAutoRefreshEnabled) {
|
|
1540
|
-
newState.tokenRefresher.start();
|
|
1541
|
-
}
|
|
1647
|
+
setState(app, Object.assign(Object.assign({}, state), { tokenObservers: [...state.tokenObservers, tokenObserver] }));
|
|
1542
1648
|
// Invoke the listener async immediately if there is a valid token
|
|
1543
1649
|
// in memory.
|
|
1544
1650
|
if (state.token && isValid(state.token)) {
|
|
1545
1651
|
const validToken = state.token;
|
|
1546
1652
|
Promise.resolve()
|
|
1547
|
-
.then(() =>
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
});
|
|
1551
|
-
}
|
|
1552
|
-
else if (state.token == null) {
|
|
1553
|
-
// Only check cache if there was no token. If the token was invalid,
|
|
1554
|
-
// skip this and rely on exchange endpoint.
|
|
1555
|
-
void state
|
|
1556
|
-
.cachedTokenPromise // Storage token promise. Always populated in `activate()`.
|
|
1557
|
-
.then(cachedToken => {
|
|
1558
|
-
if (cachedToken && isValid(cachedToken)) {
|
|
1559
|
-
listener({ token: cachedToken.token });
|
|
1560
|
-
}
|
|
1653
|
+
.then(() => {
|
|
1654
|
+
listener({ token: validToken.token });
|
|
1655
|
+
initTokenRefresher(appCheck);
|
|
1561
1656
|
})
|
|
1562
1657
|
.catch(() => {
|
|
1563
|
-
|
|
1658
|
+
/* we don't care about exceptions thrown in listeners */
|
|
1564
1659
|
});
|
|
1565
1660
|
}
|
|
1566
|
-
|
|
1661
|
+
/**
|
|
1662
|
+
* Wait for any cached token promise to resolve before starting the token
|
|
1663
|
+
* refresher. The refresher checks to see if there is an existing token
|
|
1664
|
+
* in state and calls the exchange endpoint if not. We should first let the
|
|
1665
|
+
* IndexedDB check have a chance to populate state if it can.
|
|
1666
|
+
*
|
|
1667
|
+
* Listener call isn't needed here because cachedTokenPromise will call any
|
|
1668
|
+
* listeners that exist when it resolves.
|
|
1669
|
+
*/
|
|
1670
|
+
// state.cachedTokenPromise is always populated in `activate()`.
|
|
1671
|
+
void state.cachedTokenPromise.then(() => initTokenRefresher(appCheck));
|
|
1567
1672
|
}
|
|
1568
1673
|
function removeTokenListener(app, listener) {
|
|
1569
1674
|
const state = getState(app);
|
|
@@ -1575,6 +1680,23 @@ function removeTokenListener(app, listener) {
|
|
|
1575
1680
|
}
|
|
1576
1681
|
setState(app, Object.assign(Object.assign({}, state), { tokenObservers: newObservers }));
|
|
1577
1682
|
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Logic to create and start refresher as needed.
|
|
1685
|
+
*/
|
|
1686
|
+
function initTokenRefresher(appCheck) {
|
|
1687
|
+
const { app } = appCheck;
|
|
1688
|
+
const state = getState(app);
|
|
1689
|
+
// Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
|
|
1690
|
+
// is not true.
|
|
1691
|
+
let refresher = state.tokenRefresher;
|
|
1692
|
+
if (!refresher) {
|
|
1693
|
+
refresher = createTokenRefresher(appCheck);
|
|
1694
|
+
setState(app, Object.assign(Object.assign({}, state), { tokenRefresher: refresher }));
|
|
1695
|
+
}
|
|
1696
|
+
if (!refresher.isRunning() && state.isTokenAutoRefreshEnabled) {
|
|
1697
|
+
refresher.start();
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1578
1700
|
function createTokenRefresher(appCheck) {
|
|
1579
1701
|
const { app } = appCheck;
|
|
1580
1702
|
return new Refresher(
|
|
@@ -1596,7 +1718,6 @@ function createTokenRefresher(appCheck) {
|
|
|
1596
1718
|
throw result.error;
|
|
1597
1719
|
}
|
|
1598
1720
|
}, () => {
|
|
1599
|
-
// TODO: when should we retry?
|
|
1600
1721
|
return true;
|
|
1601
1722
|
}, () => {
|
|
1602
1723
|
const state = getState(app);
|
|
@@ -1691,8 +1812,8 @@ function internalFactory(appCheck) {
|
|
|
1691
1812
|
};
|
|
1692
1813
|
}
|
|
1693
1814
|
|
|
1694
|
-
const name = "https://www.gstatic.com/firebasejs/9.
|
|
1695
|
-
const version = "0.5.
|
|
1815
|
+
const name = "https://www.gstatic.com/firebasejs/9.5.0/firebase-app.js-check";
|
|
1816
|
+
const version = "0.5.2";
|
|
1696
1817
|
|
|
1697
1818
|
/**
|
|
1698
1819
|
* @license
|
|
@@ -1850,19 +1971,44 @@ class ReCaptchaV3Provider {
|
|
|
1850
1971
|
*/
|
|
1851
1972
|
constructor(_siteKey) {
|
|
1852
1973
|
this._siteKey = _siteKey;
|
|
1974
|
+
/**
|
|
1975
|
+
* Throttle requests on certain error codes to prevent too many retries
|
|
1976
|
+
* in a short time.
|
|
1977
|
+
*/
|
|
1978
|
+
this._throttleData = null;
|
|
1853
1979
|
}
|
|
1854
1980
|
/**
|
|
1855
1981
|
* Returns an App Check token.
|
|
1856
1982
|
* @internal
|
|
1857
1983
|
*/
|
|
1858
1984
|
async getToken() {
|
|
1985
|
+
var _a;
|
|
1986
|
+
throwIfThrottled(this._throttleData);
|
|
1859
1987
|
// Top-level `getToken()` has already checked that App Check is initialized
|
|
1860
1988
|
// and therefore this._app and this._platformLoggerProvider are available.
|
|
1861
1989
|
const attestedClaimsToken = await getToken$1(this._app).catch(_e => {
|
|
1862
1990
|
// reCaptcha.execute() throws null which is not very descriptive.
|
|
1863
1991
|
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */);
|
|
1864
1992
|
});
|
|
1865
|
-
|
|
1993
|
+
let result;
|
|
1994
|
+
try {
|
|
1995
|
+
result = await exchangeToken(getExchangeRecaptchaV3TokenRequest(this._app, attestedClaimsToken), this._platformLoggerProvider);
|
|
1996
|
+
}
|
|
1997
|
+
catch (e) {
|
|
1998
|
+
if (e.code === "fetch-status-error" /* FETCH_STATUS_ERROR */) {
|
|
1999
|
+
this._throttleData = setBackoff(Number((_a = e.customData) === null || _a === void 0 ? void 0 : _a.httpStatus), this._throttleData);
|
|
2000
|
+
throw ERROR_FACTORY.create("throttled" /* THROTTLED */, {
|
|
2001
|
+
time: getDurationString(this._throttleData.allowRequestsAfter - Date.now()),
|
|
2002
|
+
httpStatus: this._throttleData.httpStatus
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
else {
|
|
2006
|
+
throw e;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
// If successful, clear throttle data.
|
|
2010
|
+
this._throttleData = null;
|
|
2011
|
+
return result;
|
|
1866
2012
|
}
|
|
1867
2013
|
/**
|
|
1868
2014
|
* @internal
|
|
@@ -1899,19 +2045,44 @@ class ReCaptchaEnterpriseProvider {
|
|
|
1899
2045
|
*/
|
|
1900
2046
|
constructor(_siteKey) {
|
|
1901
2047
|
this._siteKey = _siteKey;
|
|
2048
|
+
/**
|
|
2049
|
+
* Throttle requests on certain error codes to prevent too many retries
|
|
2050
|
+
* in a short time.
|
|
2051
|
+
*/
|
|
2052
|
+
this._throttleData = null;
|
|
1902
2053
|
}
|
|
1903
2054
|
/**
|
|
1904
2055
|
* Returns an App Check token.
|
|
1905
2056
|
* @internal
|
|
1906
2057
|
*/
|
|
1907
2058
|
async getToken() {
|
|
2059
|
+
var _a;
|
|
2060
|
+
throwIfThrottled(this._throttleData);
|
|
1908
2061
|
// Top-level `getToken()` has already checked that App Check is initialized
|
|
1909
2062
|
// and therefore this._app and this._platformLoggerProvider are available.
|
|
1910
2063
|
const attestedClaimsToken = await getToken$1(this._app).catch(_e => {
|
|
1911
2064
|
// reCaptcha.execute() throws null which is not very descriptive.
|
|
1912
2065
|
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */);
|
|
1913
2066
|
});
|
|
1914
|
-
|
|
2067
|
+
let result;
|
|
2068
|
+
try {
|
|
2069
|
+
result = await exchangeToken(getExchangeRecaptchaEnterpriseTokenRequest(this._app, attestedClaimsToken), this._platformLoggerProvider);
|
|
2070
|
+
}
|
|
2071
|
+
catch (e) {
|
|
2072
|
+
if (e.code === "fetch-status-error" /* FETCH_STATUS_ERROR */) {
|
|
2073
|
+
this._throttleData = setBackoff(Number((_a = e.customData) === null || _a === void 0 ? void 0 : _a.httpStatus), this._throttleData);
|
|
2074
|
+
throw ERROR_FACTORY.create("throttled" /* THROTTLED */, {
|
|
2075
|
+
time: getDurationString(this._throttleData.allowRequestsAfter - Date.now()),
|
|
2076
|
+
httpStatus: this._throttleData.httpStatus
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
else {
|
|
2080
|
+
throw e;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
// If successful, clear throttle data.
|
|
2084
|
+
this._throttleData = null;
|
|
2085
|
+
return result;
|
|
1915
2086
|
}
|
|
1916
2087
|
/**
|
|
1917
2088
|
* @internal
|
|
@@ -1979,6 +2150,57 @@ class CustomProvider {
|
|
|
1979
2150
|
return false;
|
|
1980
2151
|
}
|
|
1981
2152
|
}
|
|
2153
|
+
}
|
|
2154
|
+
/**
|
|
2155
|
+
* Set throttle data to block requests until after a certain time
|
|
2156
|
+
* depending on the failed request's status code.
|
|
2157
|
+
* @param httpStatus - Status code of failed request.
|
|
2158
|
+
* @param throttleData - `ThrottleData` object containing previous throttle
|
|
2159
|
+
* data state.
|
|
2160
|
+
* @returns Data about current throttle state and expiration time.
|
|
2161
|
+
*/
|
|
2162
|
+
function setBackoff(httpStatus, throttleData) {
|
|
2163
|
+
/**
|
|
2164
|
+
* Block retries for 1 day for the following error codes:
|
|
2165
|
+
*
|
|
2166
|
+
* 404: Likely malformed URL.
|
|
2167
|
+
*
|
|
2168
|
+
* 403:
|
|
2169
|
+
* - Attestation failed
|
|
2170
|
+
* - Wrong API key
|
|
2171
|
+
* - Project deleted
|
|
2172
|
+
*/
|
|
2173
|
+
if (httpStatus === 404 || httpStatus === 403) {
|
|
2174
|
+
return {
|
|
2175
|
+
backoffCount: 1,
|
|
2176
|
+
allowRequestsAfter: Date.now() + ONE_DAY,
|
|
2177
|
+
httpStatus
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
else {
|
|
2181
|
+
/**
|
|
2182
|
+
* For all other error codes, the time when it is ok to retry again
|
|
2183
|
+
* is based on exponential backoff.
|
|
2184
|
+
*/
|
|
2185
|
+
const backoffCount = throttleData ? throttleData.backoffCount : 0;
|
|
2186
|
+
const backoffMillis = calculateBackoffMillis(backoffCount, 1000, 2);
|
|
2187
|
+
return {
|
|
2188
|
+
backoffCount: backoffCount + 1,
|
|
2189
|
+
allowRequestsAfter: Date.now() + backoffMillis,
|
|
2190
|
+
httpStatus
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function throwIfThrottled(throttleData) {
|
|
2195
|
+
if (throttleData) {
|
|
2196
|
+
if (Date.now() - throttleData.allowRequestsAfter <= 0) {
|
|
2197
|
+
// If before, throw.
|
|
2198
|
+
throw ERROR_FACTORY.create("throttled" /* THROTTLED */, {
|
|
2199
|
+
time: getDurationString(throttleData.allowRequestsAfter - Date.now()),
|
|
2200
|
+
httpStatus: throttleData.httpStatus
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
1982
2204
|
}
|
|
1983
2205
|
|
|
1984
2206
|
/**
|
|
@@ -1999,7 +2221,7 @@ class CustomProvider {
|
|
|
1999
2221
|
*/
|
|
2000
2222
|
/**
|
|
2001
2223
|
* Activate App Check for the given app. Can be called only once per app.
|
|
2002
|
-
* @param app - the {@link https://www.gstatic.com/firebasejs/9.
|
|
2224
|
+
* @param app - the {@link https://www.gstatic.com/firebasejs/9.5.0/firebase-app.js#FirebaseApp} to activate App Check for
|
|
2003
2225
|
* @param options - App Check initialization options
|
|
2004
2226
|
* @public
|
|
2005
2227
|
*/
|
|
@@ -2034,6 +2256,17 @@ function initializeAppCheck(app = getApp(), options) {
|
|
|
2034
2256
|
}
|
|
2035
2257
|
const appCheck = provider.initialize({ options });
|
|
2036
2258
|
_activate(app, options.provider, options.isTokenAutoRefreshEnabled);
|
|
2259
|
+
// If isTokenAutoRefreshEnabled is false, do not send any requests to the
|
|
2260
|
+
// exchange endpoint without an explicit call from the user either directly
|
|
2261
|
+
// or through another Firebase library (storage, functions, etc.)
|
|
2262
|
+
if (getState(app).isTokenAutoRefreshEnabled) {
|
|
2263
|
+
// Adding a listener will start the refresher and fetch a token if needed.
|
|
2264
|
+
// This gets a token ready and prevents a delay when an internal library
|
|
2265
|
+
// requests the token.
|
|
2266
|
+
// Listener function does not need to do anything, its base functionality
|
|
2267
|
+
// of calling getToken() already fetches token and writes it to memory/storage.
|
|
2268
|
+
addTokenListener(appCheck, "INTERNAL" /* INTERNAL */, () => { });
|
|
2269
|
+
}
|
|
2037
2270
|
return appCheck;
|
|
2038
2271
|
}
|
|
2039
2272
|
/**
|
|
@@ -2053,6 +2286,8 @@ function _activate(app, provider, isTokenAutoRefreshEnabled) {
|
|
|
2053
2286
|
newState.cachedTokenPromise = readTokenFromStorage(app).then(cachedToken => {
|
|
2054
2287
|
if (cachedToken && isValid(cachedToken)) {
|
|
2055
2288
|
setState(app, Object.assign(Object.assign({}, getState(app)), { token: cachedToken }));
|
|
2289
|
+
// notify all listeners with the cached token
|
|
2290
|
+
notifyTokenListeners(app, { token: cachedToken.token });
|
|
2056
2291
|
}
|
|
2057
2292
|
return cachedToken;
|
|
2058
2293
|
});
|