@firebase/app-check 0.5.1 → 0.5.2
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 +6 -0
- package/dist/app-check-public.d.ts +10 -0
- package/dist/app-check.d.ts +10 -0
- package/dist/esm/index.esm.js +267 -89
- package/dist/esm/index.esm.js.map +1 -1
- package/dist/esm/index.esm2017.js +217 -47
- package/dist/esm/index.esm2017.js.map +1 -1
- package/dist/esm/src/constants.d.ts +4 -0
- package/dist/esm/src/errors.d.ts +6 -1
- package/dist/esm/src/internal-api.d.ts +1 -0
- package/dist/esm/src/providers.d.ts +10 -0
- package/dist/esm/src/providers.test.d.ts +17 -0
- package/dist/esm/src/state.d.ts +1 -0
- package/dist/esm/src/types.d.ts +5 -0
- package/dist/esm/src/util.d.ts +1 -0
- package/dist/index.cjs.js +266 -88
- package/dist/index.cjs.js.map +1 -1
- package/dist/src/constants.d.ts +4 -0
- package/dist/src/errors.d.ts +6 -1
- package/dist/src/internal-api.d.ts +1 -0
- package/dist/src/providers.d.ts +10 -0
- package/dist/src/providers.test.d.ts +17 -0
- package/dist/src/state.d.ts +1 -0
- package/dist/src/types.d.ts +5 -0
- package/dist/src/util.d.ts +1 -0
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _getProvider, getApp, _registerComponent, registerVersion } from '@firebase/app';
|
|
2
2
|
import { Component } from '@firebase/component';
|
|
3
|
-
import { Deferred, ErrorFactory, isIndexedDBAvailable, getGlobal, base64, issuedAtTime, getModularInstance } from '@firebase/util';
|
|
3
|
+
import { Deferred, ErrorFactory, isIndexedDBAvailable, getGlobal, base64, issuedAtTime, calculateBackoffMillis, getModularInstance } from '@firebase/util';
|
|
4
4
|
import { Logger } from '@firebase/logger';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -73,7 +73,11 @@ const TOKEN_REFRESH_TIME = {
|
|
|
73
73
|
* This is the maximum retrial wait, currently 16 minutes.
|
|
74
74
|
*/
|
|
75
75
|
RETRIAL_MAX_WAIT: 16 * 60 * 1000
|
|
76
|
-
};
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* One day in millis, for certain error code backoffs.
|
|
79
|
+
*/
|
|
80
|
+
const ONE_DAY = 24 * 60 * 60 * 1000;
|
|
77
81
|
|
|
78
82
|
/**
|
|
79
83
|
* @license
|
|
@@ -214,7 +218,8 @@ const ERRORS = {
|
|
|
214
218
|
["storage-open" /* STORAGE_OPEN */]: 'Error thrown when opening storage. Original error: {$originalErrorMessage}.',
|
|
215
219
|
["storage-get" /* STORAGE_GET */]: 'Error thrown when reading from storage. Original error: {$originalErrorMessage}.',
|
|
216
220
|
["storage-set" /* STORAGE_WRITE */]: 'Error thrown when writing to storage. Original error: {$originalErrorMessage}.',
|
|
217
|
-
["recaptcha-error" /* RECAPTCHA_ERROR */]: 'ReCAPTCHA error.'
|
|
221
|
+
["recaptcha-error" /* RECAPTCHA_ERROR */]: 'ReCAPTCHA error.',
|
|
222
|
+
["throttled" /* THROTTLED */]: `Requests throttled due to {$httpStatus} error. Attempts allowed again after {$time}`
|
|
218
223
|
};
|
|
219
224
|
const ERROR_FACTORY = new ErrorFactory('appCheck', 'AppCheck', ERRORS);
|
|
220
225
|
|
|
@@ -256,6 +261,28 @@ function uuidv4() {
|
|
|
256
261
|
const r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
257
262
|
return v.toString(16);
|
|
258
263
|
});
|
|
264
|
+
}
|
|
265
|
+
function getDurationString(durationInMillis) {
|
|
266
|
+
const totalSeconds = Math.round(durationInMillis / 1000);
|
|
267
|
+
const days = Math.floor(totalSeconds / (3600 * 24));
|
|
268
|
+
const hours = Math.floor((totalSeconds - days * 3600 * 24) / 3600);
|
|
269
|
+
const minutes = Math.floor((totalSeconds - days * 3600 * 24 - hours * 3600) / 60);
|
|
270
|
+
const seconds = totalSeconds - days * 3600 * 24 - hours * 3600 - minutes * 60;
|
|
271
|
+
let result = '';
|
|
272
|
+
if (days) {
|
|
273
|
+
result += pad(days) + 'd:';
|
|
274
|
+
}
|
|
275
|
+
if (hours) {
|
|
276
|
+
result += pad(hours) + 'h:';
|
|
277
|
+
}
|
|
278
|
+
result += pad(minutes) + 'm:' + pad(seconds) + 's';
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
function pad(value) {
|
|
282
|
+
if (value === 0) {
|
|
283
|
+
return '00';
|
|
284
|
+
}
|
|
285
|
+
return value >= 10 ? value.toString() : '0' + value;
|
|
259
286
|
}
|
|
260
287
|
|
|
261
288
|
/**
|
|
@@ -673,9 +700,6 @@ async function getToken$2(appCheck, forceRefresh = false) {
|
|
|
673
700
|
const cachedToken = await state.cachedTokenPromise;
|
|
674
701
|
if (cachedToken && isValid(cachedToken)) {
|
|
675
702
|
token = cachedToken;
|
|
676
|
-
setState(app, Object.assign(Object.assign({}, state), { token }));
|
|
677
|
-
// notify all listeners with the cached token
|
|
678
|
-
notifyTokenListeners(app, { token: token.token });
|
|
679
703
|
}
|
|
680
704
|
}
|
|
681
705
|
// Return the cached token (from either memory or indexedDB) if it's valid
|
|
@@ -684,13 +708,25 @@ async function getToken$2(appCheck, forceRefresh = false) {
|
|
|
684
708
|
token: token.token
|
|
685
709
|
};
|
|
686
710
|
}
|
|
711
|
+
// Only set to true if this `getToken()` call is making the actual
|
|
712
|
+
// REST call to the exchange endpoint, versus waiting for an already
|
|
713
|
+
// in-flight call (see debug and regular exchange endpoint paths below)
|
|
714
|
+
let shouldCallListeners = false;
|
|
687
715
|
/**
|
|
688
716
|
* DEBUG MODE
|
|
689
717
|
* If debug mode is set, and there is no cached token, fetch a new App
|
|
690
718
|
* Check token using the debug token, and return it directly.
|
|
691
719
|
*/
|
|
692
720
|
if (isDebugMode()) {
|
|
693
|
-
|
|
721
|
+
// Avoid making another call to the exchange endpoint if one is in flight.
|
|
722
|
+
if (!state.exchangeTokenPromise) {
|
|
723
|
+
state.exchangeTokenPromise = exchangeToken(getExchangeDebugTokenRequest(app, await getDebugToken()), appCheck.platformLoggerProvider).then(token => {
|
|
724
|
+
state.exchangeTokenPromise = undefined;
|
|
725
|
+
return token;
|
|
726
|
+
});
|
|
727
|
+
shouldCallListeners = true;
|
|
728
|
+
}
|
|
729
|
+
const tokenFromDebugExchange = await state.exchangeTokenPromise;
|
|
694
730
|
// Write debug token to indexedDB.
|
|
695
731
|
await writeTokenToStorage(app, tokenFromDebugExchange);
|
|
696
732
|
// Write debug token to state.
|
|
@@ -701,14 +737,29 @@ async function getToken$2(appCheck, forceRefresh = false) {
|
|
|
701
737
|
* request a new token
|
|
702
738
|
*/
|
|
703
739
|
try {
|
|
704
|
-
//
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
740
|
+
// Avoid making another call to the exchange endpoint if one is in flight.
|
|
741
|
+
if (!state.exchangeTokenPromise) {
|
|
742
|
+
// state.provider is populated in initializeAppCheck()
|
|
743
|
+
// ensureActivated() at the top of this function checks that
|
|
744
|
+
// initializeAppCheck() has been called.
|
|
745
|
+
state.exchangeTokenPromise = state.provider.getToken().then(token => {
|
|
746
|
+
state.exchangeTokenPromise = undefined;
|
|
747
|
+
return token;
|
|
748
|
+
});
|
|
749
|
+
shouldCallListeners = true;
|
|
750
|
+
}
|
|
751
|
+
token = await state.exchangeTokenPromise;
|
|
708
752
|
}
|
|
709
753
|
catch (e) {
|
|
710
|
-
|
|
711
|
-
|
|
754
|
+
if (e.code === `appCheck/${"throttled" /* THROTTLED */}`) {
|
|
755
|
+
// Warn if throttled, but do not treat it as an error.
|
|
756
|
+
logger.warn(e.message);
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
// `getToken()` should never throw, but logging error text to console will aid debugging.
|
|
760
|
+
logger.error(e);
|
|
761
|
+
}
|
|
762
|
+
// Always save error to be added to dummy token.
|
|
712
763
|
error = e;
|
|
713
764
|
}
|
|
714
765
|
let interopTokenResult;
|
|
@@ -726,7 +777,9 @@ async function getToken$2(appCheck, forceRefresh = false) {
|
|
|
726
777
|
setState(app, Object.assign(Object.assign({}, state), { token }));
|
|
727
778
|
await writeTokenToStorage(app, token);
|
|
728
779
|
}
|
|
729
|
-
|
|
780
|
+
if (shouldCallListeners) {
|
|
781
|
+
notifyTokenListeners(app, interopTokenResult);
|
|
782
|
+
}
|
|
730
783
|
return interopTokenResult;
|
|
731
784
|
}
|
|
732
785
|
function addTokenListener(appCheck, type, listener, onError) {
|
|
@@ -737,44 +790,31 @@ function addTokenListener(appCheck, type, listener, onError) {
|
|
|
737
790
|
error: onError,
|
|
738
791
|
type
|
|
739
792
|
};
|
|
740
|
-
|
|
741
|
-
/**
|
|
742
|
-
* Invoke the listener with the valid token, then start the token refresher
|
|
743
|
-
*/
|
|
744
|
-
if (!newState.tokenRefresher) {
|
|
745
|
-
const tokenRefresher = createTokenRefresher(appCheck);
|
|
746
|
-
newState.tokenRefresher = tokenRefresher;
|
|
747
|
-
}
|
|
748
|
-
// Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
|
|
749
|
-
// is not true.
|
|
750
|
-
if (!newState.tokenRefresher.isRunning() && state.isTokenAutoRefreshEnabled) {
|
|
751
|
-
newState.tokenRefresher.start();
|
|
752
|
-
}
|
|
793
|
+
setState(app, Object.assign(Object.assign({}, state), { tokenObservers: [...state.tokenObservers, tokenObserver] }));
|
|
753
794
|
// Invoke the listener async immediately if there is a valid token
|
|
754
795
|
// in memory.
|
|
755
796
|
if (state.token && isValid(state.token)) {
|
|
756
797
|
const validToken = state.token;
|
|
757
798
|
Promise.resolve()
|
|
758
|
-
.then(() =>
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
else if (state.token == null) {
|
|
764
|
-
// Only check cache if there was no token. If the token was invalid,
|
|
765
|
-
// skip this and rely on exchange endpoint.
|
|
766
|
-
void state
|
|
767
|
-
.cachedTokenPromise // Storage token promise. Always populated in `activate()`.
|
|
768
|
-
.then(cachedToken => {
|
|
769
|
-
if (cachedToken && isValid(cachedToken)) {
|
|
770
|
-
listener({ token: cachedToken.token });
|
|
771
|
-
}
|
|
799
|
+
.then(() => {
|
|
800
|
+
listener({ token: validToken.token });
|
|
801
|
+
initTokenRefresher(appCheck);
|
|
772
802
|
})
|
|
773
803
|
.catch(() => {
|
|
774
|
-
|
|
804
|
+
/* we don't care about exceptions thrown in listeners */
|
|
775
805
|
});
|
|
776
806
|
}
|
|
777
|
-
|
|
807
|
+
/**
|
|
808
|
+
* Wait for any cached token promise to resolve before starting the token
|
|
809
|
+
* refresher. The refresher checks to see if there is an existing token
|
|
810
|
+
* in state and calls the exchange endpoint if not. We should first let the
|
|
811
|
+
* IndexedDB check have a chance to populate state if it can.
|
|
812
|
+
*
|
|
813
|
+
* Listener call isn't needed here because cachedTokenPromise will call any
|
|
814
|
+
* listeners that exist when it resolves.
|
|
815
|
+
*/
|
|
816
|
+
// state.cachedTokenPromise is always populated in `activate()`.
|
|
817
|
+
void state.cachedTokenPromise.then(() => initTokenRefresher(appCheck));
|
|
778
818
|
}
|
|
779
819
|
function removeTokenListener(app, listener) {
|
|
780
820
|
const state = getState(app);
|
|
@@ -786,6 +826,23 @@ function removeTokenListener(app, listener) {
|
|
|
786
826
|
}
|
|
787
827
|
setState(app, Object.assign(Object.assign({}, state), { tokenObservers: newObservers }));
|
|
788
828
|
}
|
|
829
|
+
/**
|
|
830
|
+
* Logic to create and start refresher as needed.
|
|
831
|
+
*/
|
|
832
|
+
function initTokenRefresher(appCheck) {
|
|
833
|
+
const { app } = appCheck;
|
|
834
|
+
const state = getState(app);
|
|
835
|
+
// Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
|
|
836
|
+
// is not true.
|
|
837
|
+
let refresher = state.tokenRefresher;
|
|
838
|
+
if (!refresher) {
|
|
839
|
+
refresher = createTokenRefresher(appCheck);
|
|
840
|
+
setState(app, Object.assign(Object.assign({}, state), { tokenRefresher: refresher }));
|
|
841
|
+
}
|
|
842
|
+
if (!refresher.isRunning() && state.isTokenAutoRefreshEnabled) {
|
|
843
|
+
refresher.start();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
789
846
|
function createTokenRefresher(appCheck) {
|
|
790
847
|
const { app } = appCheck;
|
|
791
848
|
return new Refresher(
|
|
@@ -807,7 +864,6 @@ function createTokenRefresher(appCheck) {
|
|
|
807
864
|
throw result.error;
|
|
808
865
|
}
|
|
809
866
|
}, () => {
|
|
810
|
-
// TODO: when should we retry?
|
|
811
867
|
return true;
|
|
812
868
|
}, () => {
|
|
813
869
|
const state = getState(app);
|
|
@@ -903,7 +959,7 @@ function internalFactory(appCheck) {
|
|
|
903
959
|
}
|
|
904
960
|
|
|
905
961
|
const name = "@firebase/app-check";
|
|
906
|
-
const version = "0.5.
|
|
962
|
+
const version = "0.5.2";
|
|
907
963
|
|
|
908
964
|
/**
|
|
909
965
|
* @license
|
|
@@ -1061,19 +1117,44 @@ class ReCaptchaV3Provider {
|
|
|
1061
1117
|
*/
|
|
1062
1118
|
constructor(_siteKey) {
|
|
1063
1119
|
this._siteKey = _siteKey;
|
|
1120
|
+
/**
|
|
1121
|
+
* Throttle requests on certain error codes to prevent too many retries
|
|
1122
|
+
* in a short time.
|
|
1123
|
+
*/
|
|
1124
|
+
this._throttleData = null;
|
|
1064
1125
|
}
|
|
1065
1126
|
/**
|
|
1066
1127
|
* Returns an App Check token.
|
|
1067
1128
|
* @internal
|
|
1068
1129
|
*/
|
|
1069
1130
|
async getToken() {
|
|
1131
|
+
var _a;
|
|
1132
|
+
throwIfThrottled(this._throttleData);
|
|
1070
1133
|
// Top-level `getToken()` has already checked that App Check is initialized
|
|
1071
1134
|
// and therefore this._app and this._platformLoggerProvider are available.
|
|
1072
1135
|
const attestedClaimsToken = await getToken$1(this._app).catch(_e => {
|
|
1073
1136
|
// reCaptcha.execute() throws null which is not very descriptive.
|
|
1074
1137
|
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */);
|
|
1075
1138
|
});
|
|
1076
|
-
|
|
1139
|
+
let result;
|
|
1140
|
+
try {
|
|
1141
|
+
result = await exchangeToken(getExchangeRecaptchaV3TokenRequest(this._app, attestedClaimsToken), this._platformLoggerProvider);
|
|
1142
|
+
}
|
|
1143
|
+
catch (e) {
|
|
1144
|
+
if (e.code === "fetch-status-error" /* FETCH_STATUS_ERROR */) {
|
|
1145
|
+
this._throttleData = setBackoff(Number((_a = e.customData) === null || _a === void 0 ? void 0 : _a.httpStatus), this._throttleData);
|
|
1146
|
+
throw ERROR_FACTORY.create("throttled" /* THROTTLED */, {
|
|
1147
|
+
time: getDurationString(this._throttleData.allowRequestsAfter - Date.now()),
|
|
1148
|
+
httpStatus: this._throttleData.httpStatus
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
else {
|
|
1152
|
+
throw e;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
// If successful, clear throttle data.
|
|
1156
|
+
this._throttleData = null;
|
|
1157
|
+
return result;
|
|
1077
1158
|
}
|
|
1078
1159
|
/**
|
|
1079
1160
|
* @internal
|
|
@@ -1110,19 +1191,44 @@ class ReCaptchaEnterpriseProvider {
|
|
|
1110
1191
|
*/
|
|
1111
1192
|
constructor(_siteKey) {
|
|
1112
1193
|
this._siteKey = _siteKey;
|
|
1194
|
+
/**
|
|
1195
|
+
* Throttle requests on certain error codes to prevent too many retries
|
|
1196
|
+
* in a short time.
|
|
1197
|
+
*/
|
|
1198
|
+
this._throttleData = null;
|
|
1113
1199
|
}
|
|
1114
1200
|
/**
|
|
1115
1201
|
* Returns an App Check token.
|
|
1116
1202
|
* @internal
|
|
1117
1203
|
*/
|
|
1118
1204
|
async getToken() {
|
|
1205
|
+
var _a;
|
|
1206
|
+
throwIfThrottled(this._throttleData);
|
|
1119
1207
|
// Top-level `getToken()` has already checked that App Check is initialized
|
|
1120
1208
|
// and therefore this._app and this._platformLoggerProvider are available.
|
|
1121
1209
|
const attestedClaimsToken = await getToken$1(this._app).catch(_e => {
|
|
1122
1210
|
// reCaptcha.execute() throws null which is not very descriptive.
|
|
1123
1211
|
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */);
|
|
1124
1212
|
});
|
|
1125
|
-
|
|
1213
|
+
let result;
|
|
1214
|
+
try {
|
|
1215
|
+
result = await exchangeToken(getExchangeRecaptchaEnterpriseTokenRequest(this._app, attestedClaimsToken), this._platformLoggerProvider);
|
|
1216
|
+
}
|
|
1217
|
+
catch (e) {
|
|
1218
|
+
if (e.code === "fetch-status-error" /* FETCH_STATUS_ERROR */) {
|
|
1219
|
+
this._throttleData = setBackoff(Number((_a = e.customData) === null || _a === void 0 ? void 0 : _a.httpStatus), this._throttleData);
|
|
1220
|
+
throw ERROR_FACTORY.create("throttled" /* THROTTLED */, {
|
|
1221
|
+
time: getDurationString(this._throttleData.allowRequestsAfter - Date.now()),
|
|
1222
|
+
httpStatus: this._throttleData.httpStatus
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
else {
|
|
1226
|
+
throw e;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
// If successful, clear throttle data.
|
|
1230
|
+
this._throttleData = null;
|
|
1231
|
+
return result;
|
|
1126
1232
|
}
|
|
1127
1233
|
/**
|
|
1128
1234
|
* @internal
|
|
@@ -1190,6 +1296,57 @@ class CustomProvider {
|
|
|
1190
1296
|
return false;
|
|
1191
1297
|
}
|
|
1192
1298
|
}
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Set throttle data to block requests until after a certain time
|
|
1302
|
+
* depending on the failed request's status code.
|
|
1303
|
+
* @param httpStatus - Status code of failed request.
|
|
1304
|
+
* @param throttleData - `ThrottleData` object containing previous throttle
|
|
1305
|
+
* data state.
|
|
1306
|
+
* @returns Data about current throttle state and expiration time.
|
|
1307
|
+
*/
|
|
1308
|
+
function setBackoff(httpStatus, throttleData) {
|
|
1309
|
+
/**
|
|
1310
|
+
* Block retries for 1 day for the following error codes:
|
|
1311
|
+
*
|
|
1312
|
+
* 404: Likely malformed URL.
|
|
1313
|
+
*
|
|
1314
|
+
* 403:
|
|
1315
|
+
* - Attestation failed
|
|
1316
|
+
* - Wrong API key
|
|
1317
|
+
* - Project deleted
|
|
1318
|
+
*/
|
|
1319
|
+
if (httpStatus === 404 || httpStatus === 403) {
|
|
1320
|
+
return {
|
|
1321
|
+
backoffCount: 1,
|
|
1322
|
+
allowRequestsAfter: Date.now() + ONE_DAY,
|
|
1323
|
+
httpStatus
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
else {
|
|
1327
|
+
/**
|
|
1328
|
+
* For all other error codes, the time when it is ok to retry again
|
|
1329
|
+
* is based on exponential backoff.
|
|
1330
|
+
*/
|
|
1331
|
+
const backoffCount = throttleData ? throttleData.backoffCount : 0;
|
|
1332
|
+
const backoffMillis = calculateBackoffMillis(backoffCount, 1000, 2);
|
|
1333
|
+
return {
|
|
1334
|
+
backoffCount: backoffCount + 1,
|
|
1335
|
+
allowRequestsAfter: Date.now() + backoffMillis,
|
|
1336
|
+
httpStatus
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
function throwIfThrottled(throttleData) {
|
|
1341
|
+
if (throttleData) {
|
|
1342
|
+
if (Date.now() - throttleData.allowRequestsAfter <= 0) {
|
|
1343
|
+
// If before, throw.
|
|
1344
|
+
throw ERROR_FACTORY.create("throttled" /* THROTTLED */, {
|
|
1345
|
+
time: getDurationString(throttleData.allowRequestsAfter - Date.now()),
|
|
1346
|
+
httpStatus: throttleData.httpStatus
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1193
1350
|
}
|
|
1194
1351
|
|
|
1195
1352
|
/**
|
|
@@ -1245,6 +1402,17 @@ function initializeAppCheck(app = getApp(), options) {
|
|
|
1245
1402
|
}
|
|
1246
1403
|
const appCheck = provider.initialize({ options });
|
|
1247
1404
|
_activate(app, options.provider, options.isTokenAutoRefreshEnabled);
|
|
1405
|
+
// If isTokenAutoRefreshEnabled is false, do not send any requests to the
|
|
1406
|
+
// exchange endpoint without an explicit call from the user either directly
|
|
1407
|
+
// or through another Firebase library (storage, functions, etc.)
|
|
1408
|
+
if (getState(app).isTokenAutoRefreshEnabled) {
|
|
1409
|
+
// Adding a listener will start the refresher and fetch a token if needed.
|
|
1410
|
+
// This gets a token ready and prevents a delay when an internal library
|
|
1411
|
+
// requests the token.
|
|
1412
|
+
// Listener function does not need to do anything, its base functionality
|
|
1413
|
+
// of calling getToken() already fetches token and writes it to memory/storage.
|
|
1414
|
+
addTokenListener(appCheck, "INTERNAL" /* INTERNAL */, () => { });
|
|
1415
|
+
}
|
|
1248
1416
|
return appCheck;
|
|
1249
1417
|
}
|
|
1250
1418
|
/**
|
|
@@ -1264,6 +1432,8 @@ function _activate(app, provider, isTokenAutoRefreshEnabled) {
|
|
|
1264
1432
|
newState.cachedTokenPromise = readTokenFromStorage(app).then(cachedToken => {
|
|
1265
1433
|
if (cachedToken && isValid(cachedToken)) {
|
|
1266
1434
|
setState(app, Object.assign(Object.assign({}, getState(app)), { token: cachedToken }));
|
|
1435
|
+
// notify all listeners with the cached token
|
|
1436
|
+
notifyTokenListeners(app, { token: cachedToken.token });
|
|
1267
1437
|
}
|
|
1268
1438
|
return cachedToken;
|
|
1269
1439
|
});
|