summaryrefslogtreecommitdiff
path: root/frontend-old/node_modules/@firebase/app-check/dist/index.cjs.js
diff options
context:
space:
mode:
authoraltaf-creator <dev@altafcreator.com>2025-11-09 11:15:19 +0800
committeraltaf-creator <dev@altafcreator.com>2025-11-09 11:15:19 +0800
commit8eff962cab608341a6f2fedc640a0e32d96f26e2 (patch)
tree05534d1a720ddc3691d346c69b4972555820a061 /frontend-old/node_modules/@firebase/app-check/dist/index.cjs.js
pain
Diffstat (limited to 'frontend-old/node_modules/@firebase/app-check/dist/index.cjs.js')
-rw-r--r--frontend-old/node_modules/@firebase/app-check/dist/index.cjs.js1704
1 files changed, 1704 insertions, 0 deletions
diff --git a/frontend-old/node_modules/@firebase/app-check/dist/index.cjs.js b/frontend-old/node_modules/@firebase/app-check/dist/index.cjs.js
new file mode 100644
index 0000000..340b411
--- /dev/null
+++ b/frontend-old/node_modules/@firebase/app-check/dist/index.cjs.js
@@ -0,0 +1,1704 @@
+'use strict';
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+var app = require('@firebase/app');
+var component = require('@firebase/component');
+var util = require('@firebase/util');
+var logger$1 = require('@firebase/logger');
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const APP_CHECK_STATES = new Map();
+const DEFAULT_STATE = {
+ activated: false,
+ tokenObservers: []
+};
+const DEBUG_STATE = {
+ initialized: false,
+ enabled: false
+};
+/**
+ * Gets a reference to the state object.
+ */
+function getStateReference(app) {
+ return APP_CHECK_STATES.get(app) || { ...DEFAULT_STATE };
+}
+/**
+ * Set once on initialization. The map should hold the same reference to the
+ * same object until this entry is deleted.
+ */
+function setInitialState(app, state) {
+ APP_CHECK_STATES.set(app, state);
+ return APP_CHECK_STATES.get(app);
+}
+function getDebugState() {
+ return DEBUG_STATE;
+}
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const BASE_ENDPOINT = 'https://content-firebaseappcheck.googleapis.com/v1';
+const EXCHANGE_RECAPTCHA_TOKEN_METHOD = 'exchangeRecaptchaV3Token';
+const EXCHANGE_RECAPTCHA_ENTERPRISE_TOKEN_METHOD = 'exchangeRecaptchaEnterpriseToken';
+const EXCHANGE_DEBUG_TOKEN_METHOD = 'exchangeDebugToken';
+const TOKEN_REFRESH_TIME = {
+ /**
+ * The offset time before token natural expiration to run the refresh.
+ * This is currently 5 minutes.
+ */
+ OFFSET_DURATION: 5 * 60 * 1000,
+ /**
+ * This is the first retrial wait after an error. This is currently
+ * 30 seconds.
+ */
+ RETRIAL_MIN_WAIT: 30 * 1000,
+ /**
+ * This is the maximum retrial wait, currently 16 minutes.
+ */
+ RETRIAL_MAX_WAIT: 16 * 60 * 1000
+};
+/**
+ * One day in millis, for certain error code backoffs.
+ */
+const ONE_DAY = 24 * 60 * 60 * 1000;
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Port from auth proactiverefresh.js
+ *
+ */
+// TODO: move it to @firebase/util?
+// TODO: allow to config whether refresh should happen in the background
+class Refresher {
+ constructor(operation, retryPolicy, getWaitDuration, lowerBound, upperBound) {
+ this.operation = operation;
+ this.retryPolicy = retryPolicy;
+ this.getWaitDuration = getWaitDuration;
+ this.lowerBound = lowerBound;
+ this.upperBound = upperBound;
+ this.pending = null;
+ this.nextErrorWaitInterval = lowerBound;
+ if (lowerBound > upperBound) {
+ throw new Error('Proactive refresh lower bound greater than upper bound!');
+ }
+ }
+ start() {
+ this.nextErrorWaitInterval = this.lowerBound;
+ this.process(true).catch(() => {
+ /* we don't care about the result */
+ });
+ }
+ stop() {
+ if (this.pending) {
+ this.pending.reject('cancelled');
+ this.pending = null;
+ }
+ }
+ isRunning() {
+ return !!this.pending;
+ }
+ async process(hasSucceeded) {
+ this.stop();
+ try {
+ this.pending = new util.Deferred();
+ this.pending.promise.catch(_e => {
+ /* ignore */
+ });
+ await sleep(this.getNextRun(hasSucceeded));
+ // Why do we resolve a promise, then immediate wait for it?
+ // We do it to make the promise chain cancellable.
+ // We can call stop() which rejects the promise before the following line execute, which makes
+ // the code jump to the catch block.
+ // TODO: unit test this
+ this.pending.resolve();
+ await this.pending.promise;
+ this.pending = new util.Deferred();
+ this.pending.promise.catch(_e => {
+ /* ignore */
+ });
+ await this.operation();
+ this.pending.resolve();
+ await this.pending.promise;
+ this.process(true).catch(() => {
+ /* we don't care about the result */
+ });
+ }
+ catch (error) {
+ if (this.retryPolicy(error)) {
+ this.process(false).catch(() => {
+ /* we don't care about the result */
+ });
+ }
+ else {
+ this.stop();
+ }
+ }
+ }
+ getNextRun(hasSucceeded) {
+ if (hasSucceeded) {
+ // If last operation succeeded, reset next error wait interval and return
+ // the default wait duration.
+ this.nextErrorWaitInterval = this.lowerBound;
+ // Return typical wait duration interval after a successful operation.
+ return this.getWaitDuration();
+ }
+ else {
+ // Get next error wait interval.
+ const currentErrorWaitInterval = this.nextErrorWaitInterval;
+ // Double interval for next consecutive error.
+ this.nextErrorWaitInterval *= 2;
+ // Make sure next wait interval does not exceed the maximum upper bound.
+ if (this.nextErrorWaitInterval > this.upperBound) {
+ this.nextErrorWaitInterval = this.upperBound;
+ }
+ return currentErrorWaitInterval;
+ }
+ }
+}
+function sleep(ms) {
+ return new Promise(resolve => {
+ setTimeout(resolve, ms);
+ });
+}
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const ERRORS = {
+ ["already-initialized" /* AppCheckError.ALREADY_INITIALIZED */]: 'You have already called initializeAppCheck() for FirebaseApp {$appName} with ' +
+ 'different options. To avoid this error, call initializeAppCheck() with the ' +
+ 'same options as when it was originally called. This will return the ' +
+ 'already initialized instance.',
+ ["use-before-activation" /* AppCheckError.USE_BEFORE_ACTIVATION */]: 'App Check is being used before initializeAppCheck() is called for FirebaseApp {$appName}. ' +
+ 'Call initializeAppCheck() before instantiating other Firebase services.',
+ ["fetch-network-error" /* AppCheckError.FETCH_NETWORK_ERROR */]: 'Fetch failed to connect to a network. Check Internet connection. ' +
+ 'Original error: {$originalErrorMessage}.',
+ ["fetch-parse-error" /* AppCheckError.FETCH_PARSE_ERROR */]: 'Fetch client could not parse response.' +
+ ' Original error: {$originalErrorMessage}.',
+ ["fetch-status-error" /* AppCheckError.FETCH_STATUS_ERROR */]: 'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.',
+ ["storage-open" /* AppCheckError.STORAGE_OPEN */]: 'Error thrown when opening storage. Original error: {$originalErrorMessage}.',
+ ["storage-get" /* AppCheckError.STORAGE_GET */]: 'Error thrown when reading from storage. Original error: {$originalErrorMessage}.',
+ ["storage-set" /* AppCheckError.STORAGE_WRITE */]: 'Error thrown when writing to storage. Original error: {$originalErrorMessage}.',
+ ["recaptcha-error" /* AppCheckError.RECAPTCHA_ERROR */]: 'ReCAPTCHA error.',
+ ["initial-throttle" /* AppCheckError.INITIAL_THROTTLE */]: `{$httpStatus} error. Attempts allowed again after {$time}`,
+ ["throttled" /* AppCheckError.THROTTLED */]: `Requests throttled due to previous {$httpStatus} error. Attempts allowed again after {$time}`
+};
+const ERROR_FACTORY = new util.ErrorFactory('appCheck', 'AppCheck', ERRORS);
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+function getRecaptcha(isEnterprise = false) {
+ if (isEnterprise) {
+ return self.grecaptcha?.enterprise;
+ }
+ return self.grecaptcha;
+}
+function ensureActivated(app) {
+ if (!getStateReference(app).activated) {
+ throw ERROR_FACTORY.create("use-before-activation" /* AppCheckError.USE_BEFORE_ACTIVATION */, {
+ appName: app.name
+ });
+ }
+}
+function getDurationString(durationInMillis) {
+ const totalSeconds = Math.round(durationInMillis / 1000);
+ const days = Math.floor(totalSeconds / (3600 * 24));
+ const hours = Math.floor((totalSeconds - days * 3600 * 24) / 3600);
+ const minutes = Math.floor((totalSeconds - days * 3600 * 24 - hours * 3600) / 60);
+ const seconds = totalSeconds - days * 3600 * 24 - hours * 3600 - minutes * 60;
+ let result = '';
+ if (days) {
+ result += pad(days) + 'd:';
+ }
+ if (hours) {
+ result += pad(hours) + 'h:';
+ }
+ result += pad(minutes) + 'm:' + pad(seconds) + 's';
+ return result;
+}
+function pad(value) {
+ if (value === 0) {
+ return '00';
+ }
+ return value >= 10 ? value.toString() : '0' + value;
+}
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+async function exchangeToken({ url, body }, heartbeatServiceProvider) {
+ const headers = {
+ 'Content-Type': 'application/json'
+ };
+ // If heartbeat service exists, add heartbeat header string to the header.
+ const heartbeatService = heartbeatServiceProvider.getImmediate({
+ optional: true
+ });
+ if (heartbeatService) {
+ const heartbeatsHeader = await heartbeatService.getHeartbeatsHeader();
+ if (heartbeatsHeader) {
+ headers['X-Firebase-Client'] = heartbeatsHeader;
+ }
+ }
+ const options = {
+ method: 'POST',
+ body: JSON.stringify(body),
+ headers
+ };
+ let response;
+ try {
+ response = await fetch(url, options);
+ }
+ catch (originalError) {
+ throw ERROR_FACTORY.create("fetch-network-error" /* AppCheckError.FETCH_NETWORK_ERROR */, {
+ originalErrorMessage: originalError?.message
+ });
+ }
+ if (response.status !== 200) {
+ throw ERROR_FACTORY.create("fetch-status-error" /* AppCheckError.FETCH_STATUS_ERROR */, {
+ httpStatus: response.status
+ });
+ }
+ let responseBody;
+ try {
+ // JSON parsing throws SyntaxError if the response body isn't a JSON string.
+ responseBody = await response.json();
+ }
+ catch (originalError) {
+ throw ERROR_FACTORY.create("fetch-parse-error" /* AppCheckError.FETCH_PARSE_ERROR */, {
+ originalErrorMessage: originalError?.message
+ });
+ }
+ // Protobuf duration format.
+ // https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/Duration
+ const match = responseBody.ttl.match(/^([\d.]+)(s)$/);
+ if (!match || !match[2] || isNaN(Number(match[1]))) {
+ throw ERROR_FACTORY.create("fetch-parse-error" /* AppCheckError.FETCH_PARSE_ERROR */, {
+ originalErrorMessage: `ttl field (timeToLive) is not in standard Protobuf Duration ` +
+ `format: ${responseBody.ttl}`
+ });
+ }
+ const timeToLiveAsNumber = Number(match[1]) * 1000;
+ const now = Date.now();
+ return {
+ token: responseBody.token,
+ expireTimeMillis: now + timeToLiveAsNumber,
+ issuedAtTimeMillis: now
+ };
+}
+function getExchangeRecaptchaV3TokenRequest(app, reCAPTCHAToken) {
+ const { projectId, appId, apiKey } = app.options;
+ return {
+ url: `${BASE_ENDPOINT}/projects/${projectId}/apps/${appId}:${EXCHANGE_RECAPTCHA_TOKEN_METHOD}?key=${apiKey}`,
+ body: {
+ 'recaptcha_v3_token': reCAPTCHAToken
+ }
+ };
+}
+function getExchangeRecaptchaEnterpriseTokenRequest(app, reCAPTCHAToken) {
+ const { projectId, appId, apiKey } = app.options;
+ return {
+ url: `${BASE_ENDPOINT}/projects/${projectId}/apps/${appId}:${EXCHANGE_RECAPTCHA_ENTERPRISE_TOKEN_METHOD}?key=${apiKey}`,
+ body: {
+ 'recaptcha_enterprise_token': reCAPTCHAToken
+ }
+ };
+}
+function getExchangeDebugTokenRequest(app, debugToken) {
+ const { projectId, appId, apiKey } = app.options;
+ return {
+ url: `${BASE_ENDPOINT}/projects/${projectId}/apps/${appId}:${EXCHANGE_DEBUG_TOKEN_METHOD}?key=${apiKey}`,
+ body: {
+ // eslint-disable-next-line
+ debug_token: debugToken
+ }
+ };
+}
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const DB_NAME = 'firebase-app-check-database';
+const DB_VERSION = 1;
+const STORE_NAME = 'firebase-app-check-store';
+const DEBUG_TOKEN_KEY = 'debug-token';
+let dbPromise = null;
+function getDBPromise() {
+ if (dbPromise) {
+ return dbPromise;
+ }
+ dbPromise = new Promise((resolve, reject) => {
+ try {
+ const request = indexedDB.open(DB_NAME, DB_VERSION);
+ request.onsuccess = event => {
+ resolve(event.target.result);
+ };
+ request.onerror = event => {
+ reject(ERROR_FACTORY.create("storage-open" /* AppCheckError.STORAGE_OPEN */, {
+ originalErrorMessage: event.target.error?.message
+ }));
+ };
+ request.onupgradeneeded = event => {
+ const db = event.target.result;
+ // We don't use 'break' in this switch statement, the fall-through
+ // behavior is what we want, because if there are multiple versions between
+ // the old version and the current version, we want ALL the migrations
+ // that correspond to those versions to run, not only the last one.
+ // eslint-disable-next-line default-case
+ switch (event.oldVersion) {
+ case 0:
+ db.createObjectStore(STORE_NAME, {
+ keyPath: 'compositeKey'
+ });
+ }
+ };
+ }
+ catch (e) {
+ reject(ERROR_FACTORY.create("storage-open" /* AppCheckError.STORAGE_OPEN */, {
+ originalErrorMessage: e?.message
+ }));
+ }
+ });
+ return dbPromise;
+}
+function readTokenFromIndexedDB(app) {
+ return read(computeKey(app));
+}
+function writeTokenToIndexedDB(app, token) {
+ return write(computeKey(app), token);
+}
+function writeDebugTokenToIndexedDB(token) {
+ return write(DEBUG_TOKEN_KEY, token);
+}
+function readDebugTokenFromIndexedDB() {
+ return read(DEBUG_TOKEN_KEY);
+}
+async function write(key, value) {
+ const db = await getDBPromise();
+ const transaction = db.transaction(STORE_NAME, 'readwrite');
+ const store = transaction.objectStore(STORE_NAME);
+ const request = store.put({
+ compositeKey: key,
+ value
+ });
+ return new Promise((resolve, reject) => {
+ request.onsuccess = _event => {
+ resolve();
+ };
+ transaction.onerror = event => {
+ reject(ERROR_FACTORY.create("storage-set" /* AppCheckError.STORAGE_WRITE */, {
+ originalErrorMessage: event.target.error?.message
+ }));
+ };
+ });
+}
+async function read(key) {
+ const db = await getDBPromise();
+ const transaction = db.transaction(STORE_NAME, 'readonly');
+ const store = transaction.objectStore(STORE_NAME);
+ const request = store.get(key);
+ return new Promise((resolve, reject) => {
+ request.onsuccess = event => {
+ const result = event.target.result;
+ if (result) {
+ resolve(result.value);
+ }
+ else {
+ resolve(undefined);
+ }
+ };
+ transaction.onerror = event => {
+ reject(ERROR_FACTORY.create("storage-get" /* AppCheckError.STORAGE_GET */, {
+ originalErrorMessage: event.target.error?.message
+ }));
+ };
+ });
+}
+function computeKey(app) {
+ return `${app.options.appId}-${app.name}`;
+}
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const logger = new logger$1.Logger('@firebase/app-check');
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Always resolves. In case of an error reading from indexeddb, resolve with undefined
+ */
+async function readTokenFromStorage(app) {
+ if (util.isIndexedDBAvailable()) {
+ let token = undefined;
+ try {
+ token = await readTokenFromIndexedDB(app);
+ }
+ catch (e) {
+ // swallow the error and return undefined
+ logger.warn(`Failed to read token from IndexedDB. Error: ${e}`);
+ }
+ return token;
+ }
+ return undefined;
+}
+/**
+ * Always resolves. In case of an error writing to indexeddb, print a warning and resolve the promise
+ */
+function writeTokenToStorage(app, token) {
+ if (util.isIndexedDBAvailable()) {
+ return writeTokenToIndexedDB(app, token).catch(e => {
+ // swallow the error and resolve the promise
+ logger.warn(`Failed to write token to IndexedDB. Error: ${e}`);
+ });
+ }
+ return Promise.resolve();
+}
+async function readOrCreateDebugTokenFromStorage() {
+ /**
+ * Theoretically race condition can happen if we read, then write in 2 separate transactions.
+ * But it won't happen here, because this function will be called exactly once.
+ */
+ let existingDebugToken = undefined;
+ try {
+ existingDebugToken = await readDebugTokenFromIndexedDB();
+ }
+ catch (_e) {
+ // failed to read from indexeddb. We assume there is no existing debug token, and generate a new one.
+ }
+ if (!existingDebugToken) {
+ // create a new debug token
+ // This function is only available in secure contexts. See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
+ const newToken = crypto.randomUUID();
+ // We don't need to block on writing to indexeddb
+ // In case persistence failed, a new debug token will be generated every time the page is refreshed.
+ // It renders the debug token useless because you have to manually register(whitelist) the new token in the firebase console again and again.
+ // If you see this error trying to use debug token, it probably means you are using a browser that doesn't support indexeddb.
+ // You should switch to a different browser that supports indexeddb
+ writeDebugTokenToIndexedDB(newToken).catch(e => logger.warn(`Failed to persist debug token to IndexedDB. Error: ${e}`));
+ return newToken;
+ }
+ else {
+ return existingDebugToken;
+ }
+}
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+function isDebugMode() {
+ const debugState = getDebugState();
+ return debugState.enabled;
+}
+async function getDebugToken() {
+ const state = getDebugState();
+ if (state.enabled && state.token) {
+ return state.token.promise;
+ }
+ else {
+ // should not happen!
+ throw Error(`
+ Can't get debug token in production mode.
+ `);
+ }
+}
+function initializeDebugMode() {
+ const globals = util.getGlobal();
+ const debugState = getDebugState();
+ // Set to true if this function has been called, whether or not
+ // it enabled debug mode.
+ debugState.initialized = true;
+ if (typeof globals.FIREBASE_APPCHECK_DEBUG_TOKEN !== 'string' &&
+ globals.FIREBASE_APPCHECK_DEBUG_TOKEN !== true) {
+ return;
+ }
+ debugState.enabled = true;
+ const deferredToken = new util.Deferred();
+ debugState.token = deferredToken;
+ if (typeof globals.FIREBASE_APPCHECK_DEBUG_TOKEN === 'string') {
+ deferredToken.resolve(globals.FIREBASE_APPCHECK_DEBUG_TOKEN);
+ }
+ else {
+ deferredToken.resolve(readOrCreateDebugTokenFromStorage());
+ }
+}
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Initial hardcoded value agreed upon across platforms for initial launch.
+// Format left open for possible dynamic error values and other fields in the future.
+const defaultTokenErrorData = { error: 'UNKNOWN_ERROR' };
+/**
+ * Stringify and base64 encode token error data.
+ *
+ * @param tokenError Error data, currently hardcoded.
+ */
+function formatDummyToken(tokenErrorData) {
+ return util.base64.encodeString(JSON.stringify(tokenErrorData),
+ /* webSafe= */ false);
+}
+/**
+ * This function always resolves.
+ * The result will contain an error field if there is any error.
+ * In case there is an error, the token field in the result will be populated with a dummy value
+ */
+async function getToken$2(appCheck, forceRefresh = false, shouldLogErrors = false) {
+ const app = appCheck.app;
+ ensureActivated(app);
+ const state = getStateReference(app);
+ /**
+ * First check if there is a token in memory from a previous `getToken()` call.
+ */
+ let token = state.token;
+ let error = undefined;
+ /**
+ * If an invalid token was found in memory, clear token from
+ * memory and unset the local variable `token`.
+ */
+ if (token && !isValid(token)) {
+ state.token = undefined;
+ token = undefined;
+ }
+ /**
+ * If there is no valid token in memory, try to load token from indexedDB.
+ */
+ if (!token) {
+ // cachedTokenPromise contains the token found in IndexedDB or undefined if not found.
+ const cachedToken = await state.cachedTokenPromise;
+ if (cachedToken) {
+ if (isValid(cachedToken)) {
+ token = cachedToken;
+ }
+ else {
+ // If there was an invalid token in the indexedDB cache, clear it.
+ await writeTokenToStorage(app, undefined);
+ }
+ }
+ }
+ // Return the cached token (from either memory or indexedDB) if it's valid
+ if (!forceRefresh && token && isValid(token)) {
+ return {
+ token: token.token
+ };
+ }
+ // Only set to true if this `getToken()` call is making the actual
+ // REST call to the exchange endpoint, versus waiting for an already
+ // in-flight call (see debug and regular exchange endpoint paths below)
+ let shouldCallListeners = false;
+ /**
+ * DEBUG MODE
+ * If debug mode is set, and there is no cached token, fetch a new App
+ * Check token using the debug token, and return it directly.
+ */
+ if (isDebugMode()) {
+ try {
+ // Avoid making another call to the exchange endpoint if one is in flight.
+ if (!state.exchangeTokenPromise) {
+ state.exchangeTokenPromise = exchangeToken(getExchangeDebugTokenRequest(app, await getDebugToken()), appCheck.heartbeatServiceProvider).finally(() => {
+ // Clear promise when settled - either resolved or rejected.
+ state.exchangeTokenPromise = undefined;
+ });
+ shouldCallListeners = true;
+ }
+ const tokenFromDebugExchange = await state.exchangeTokenPromise;
+ // Write debug token to indexedDB.
+ await writeTokenToStorage(app, tokenFromDebugExchange);
+ // Write debug token to state.
+ state.token = tokenFromDebugExchange;
+ return { token: tokenFromDebugExchange.token };
+ }
+ catch (e) {
+ if (e.code === `appCheck/${"throttled" /* AppCheckError.THROTTLED */}` ||
+ e.code ===
+ `appCheck/${"initial-throttle" /* AppCheckError.INITIAL_THROTTLE */}`) {
+ // Warn if throttled, but do not treat it as an error.
+ logger.warn(e.message);
+ }
+ else if (shouldLogErrors) {
+ logger.error(e);
+ }
+ // Return dummy token and error
+ return makeDummyTokenResult(e);
+ }
+ }
+ /**
+ * There are no valid tokens in memory or indexedDB and we are not in
+ * debug mode.
+ * Request a new token from the exchange endpoint.
+ */
+ try {
+ // Avoid making another call to the exchange endpoint if one is in flight.
+ if (!state.exchangeTokenPromise) {
+ // state.provider is populated in initializeAppCheck()
+ // ensureActivated() at the top of this function checks that
+ // initializeAppCheck() has been called.
+ state.exchangeTokenPromise = state.provider.getToken().finally(() => {
+ // Clear promise when settled - either resolved or rejected.
+ state.exchangeTokenPromise = undefined;
+ });
+ shouldCallListeners = true;
+ }
+ token = await getStateReference(app).exchangeTokenPromise;
+ }
+ catch (e) {
+ if (e.code === `appCheck/${"throttled" /* AppCheckError.THROTTLED */}` ||
+ e.code === `appCheck/${"initial-throttle" /* AppCheckError.INITIAL_THROTTLE */}`) {
+ // Warn if throttled, but do not treat it as an error.
+ logger.warn(e.message);
+ }
+ else if (shouldLogErrors) {
+ logger.error(e);
+ }
+ // Always save error to be added to dummy token.
+ error = e;
+ }
+ let interopTokenResult;
+ if (!token) {
+ // If token is undefined, there must be an error.
+ // Return a dummy token along with the error.
+ interopTokenResult = makeDummyTokenResult(error);
+ }
+ else if (error) {
+ if (isValid(token)) {
+ // It's also possible a valid token exists, but there's also an error.
+ // (Such as if the token is almost expired, tries to refresh, and
+ // the exchange request fails.)
+ // We add a special error property here so that the refresher will
+ // count this as a failed attempt and use the backoff instead of
+ // retrying repeatedly with no delay, but any 3P listeners will not
+ // be hindered in getting the still-valid token.
+ interopTokenResult = {
+ token: token.token,
+ internalError: error
+ };
+ }
+ else {
+ // No invalid tokens should make it to this step. Memory and cached tokens
+ // are checked. Other tokens are from fresh exchanges. But just in case.
+ interopTokenResult = makeDummyTokenResult(error);
+ }
+ }
+ else {
+ interopTokenResult = {
+ token: token.token
+ };
+ // write the new token to the memory state as well as the persistent storage.
+ // Only do it if we got a valid new token
+ state.token = token;
+ await writeTokenToStorage(app, token);
+ }
+ if (shouldCallListeners) {
+ notifyTokenListeners(app, interopTokenResult);
+ }
+ return interopTokenResult;
+}
+/**
+ * Internal API for limited use tokens. Skips all FAC state and simply calls
+ * the underlying provider.
+ */
+async function getLimitedUseToken$1(appCheck) {
+ const app = appCheck.app;
+ ensureActivated(app);
+ const { provider } = getStateReference(app);
+ if (isDebugMode()) {
+ const debugToken = await getDebugToken();
+ const { token } = await exchangeToken(getExchangeDebugTokenRequest(app, debugToken), appCheck.heartbeatServiceProvider);
+ return { token };
+ }
+ else {
+ // provider is definitely valid since we ensure AppCheck was activated
+ const { token } = await provider.getToken();
+ return { token };
+ }
+}
+function addTokenListener(appCheck, type, listener, onError) {
+ const { app } = appCheck;
+ const state = getStateReference(app);
+ const tokenObserver = {
+ next: listener,
+ error: onError,
+ type
+ };
+ state.tokenObservers = [...state.tokenObservers, tokenObserver];
+ // Invoke the listener async immediately if there is a valid token
+ // in memory.
+ if (state.token && isValid(state.token)) {
+ const validToken = state.token;
+ Promise.resolve()
+ .then(() => {
+ listener({ token: validToken.token });
+ initTokenRefresher(appCheck);
+ })
+ .catch(() => {
+ /* we don't care about exceptions thrown in listeners */
+ });
+ }
+ /**
+ * Wait for any cached token promise to resolve before starting the token
+ * refresher. The refresher checks to see if there is an existing token
+ * in state and calls the exchange endpoint if not. We should first let the
+ * IndexedDB check have a chance to populate state if it can.
+ *
+ * Listener call isn't needed here because cachedTokenPromise will call any
+ * listeners that exist when it resolves.
+ */
+ // state.cachedTokenPromise is always populated in `activate()`.
+ void state.cachedTokenPromise.then(() => initTokenRefresher(appCheck));
+}
+function removeTokenListener(app, listener) {
+ const state = getStateReference(app);
+ const newObservers = state.tokenObservers.filter(tokenObserver => tokenObserver.next !== listener);
+ if (newObservers.length === 0 &&
+ state.tokenRefresher &&
+ state.tokenRefresher.isRunning()) {
+ state.tokenRefresher.stop();
+ }
+ state.tokenObservers = newObservers;
+}
+/**
+ * Logic to create and start refresher as needed.
+ */
+function initTokenRefresher(appCheck) {
+ const { app } = appCheck;
+ const state = getStateReference(app);
+ // Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
+ // is not true.
+ let refresher = state.tokenRefresher;
+ if (!refresher) {
+ refresher = createTokenRefresher(appCheck);
+ state.tokenRefresher = refresher;
+ }
+ if (!refresher.isRunning() && state.isTokenAutoRefreshEnabled) {
+ refresher.start();
+ }
+}
+function createTokenRefresher(appCheck) {
+ const { app } = appCheck;
+ return new Refresher(
+ // Keep in mind when this fails for any reason other than the ones
+ // for which we should retry, it will effectively stop the proactive refresh.
+ async () => {
+ const state = getStateReference(app);
+ // If there is no token, we will try to load it from storage and use it
+ // If there is a token, we force refresh it because we know it's going to expire soon
+ let result;
+ if (!state.token) {
+ result = await getToken$2(appCheck);
+ }
+ else {
+ result = await getToken$2(appCheck, true);
+ }
+ /**
+ * getToken() always resolves. In case the result has an error field defined, it means
+ * the operation failed, and we should retry.
+ */
+ if (result.error) {
+ throw result.error;
+ }
+ /**
+ * A special `internalError` field reflects that there was an error
+ * getting a new token from the exchange endpoint, but there's still a
+ * previous token that's valid for now and this should be passed to 2P/3P
+ * requests for a token. But we want this callback (`this.operation` in
+ * `Refresher`) to throw in order to kick off the Refresher's retry
+ * backoff. (Setting `hasSucceeded` to false.)
+ */
+ if (result.internalError) {
+ throw result.internalError;
+ }
+ }, () => {
+ return true;
+ }, () => {
+ const state = getStateReference(app);
+ if (state.token) {
+ // issuedAtTime + (50% * total TTL) + 5 minutes
+ let nextRefreshTimeMillis = state.token.issuedAtTimeMillis +
+ (state.token.expireTimeMillis - state.token.issuedAtTimeMillis) *
+ 0.5 +
+ 5 * 60 * 1000;
+ // Do not allow refresh time to be past (expireTime - 5 minutes)
+ const latestAllowableRefresh = state.token.expireTimeMillis - 5 * 60 * 1000;
+ nextRefreshTimeMillis = Math.min(nextRefreshTimeMillis, latestAllowableRefresh);
+ return Math.max(0, nextRefreshTimeMillis - Date.now());
+ }
+ else {
+ return 0;
+ }
+ }, TOKEN_REFRESH_TIME.RETRIAL_MIN_WAIT, TOKEN_REFRESH_TIME.RETRIAL_MAX_WAIT);
+}
+function notifyTokenListeners(app, token) {
+ const observers = getStateReference(app).tokenObservers;
+ for (const observer of observers) {
+ try {
+ if (observer.type === "EXTERNAL" /* ListenerType.EXTERNAL */ && token.error != null) {
+ // If this listener was added by a 3P call, send any token error to
+ // the supplied error handler. A 3P observer always has an error
+ // handler.
+ observer.error(token.error);
+ }
+ else {
+ // If the token has no error field, always return the token.
+ // If this is a 2P listener, return the token, whether or not it
+ // has an error field.
+ observer.next(token);
+ }
+ }
+ catch (e) {
+ // Errors in the listener function itself are always ignored.
+ }
+ }
+}
+function isValid(token) {
+ return token.expireTimeMillis - Date.now() > 0;
+}
+function makeDummyTokenResult(error) {
+ return {
+ token: formatDummyToken(defaultTokenErrorData),
+ error
+ };
+}
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * AppCheck Service class.
+ */
+class AppCheckService {
+ constructor(app, heartbeatServiceProvider) {
+ this.app = app;
+ this.heartbeatServiceProvider = heartbeatServiceProvider;
+ }
+ _delete() {
+ const { tokenObservers } = getStateReference(this.app);
+ for (const tokenObserver of tokenObservers) {
+ removeTokenListener(this.app, tokenObserver.next);
+ }
+ return Promise.resolve();
+ }
+}
+function factory(app, heartbeatServiceProvider) {
+ return new AppCheckService(app, heartbeatServiceProvider);
+}
+function internalFactory(appCheck) {
+ return {
+ getToken: forceRefresh => getToken$2(appCheck, forceRefresh),
+ getLimitedUseToken: () => getLimitedUseToken$1(appCheck),
+ addTokenListener: listener => addTokenListener(appCheck, "INTERNAL" /* ListenerType.INTERNAL */, listener),
+ removeTokenListener: listener => removeTokenListener(appCheck.app, listener)
+ };
+}
+
+const name = "@firebase/app-check";
+const version = "0.11.0";
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const RECAPTCHA_URL = 'https://www.google.com/recaptcha/api.js';
+const RECAPTCHA_ENTERPRISE_URL = 'https://www.google.com/recaptcha/enterprise.js';
+function initializeV3(app, siteKey) {
+ const initialized = new util.Deferred();
+ const state = getStateReference(app);
+ state.reCAPTCHAState = { initialized };
+ const divId = makeDiv(app);
+ const grecaptcha = getRecaptcha(false);
+ if (!grecaptcha) {
+ loadReCAPTCHAV3Script(() => {
+ const grecaptcha = getRecaptcha(false);
+ if (!grecaptcha) {
+ // it shouldn't happen.
+ throw new Error('no recaptcha');
+ }
+ queueWidgetRender(app, siteKey, grecaptcha, divId, initialized);
+ });
+ }
+ else {
+ queueWidgetRender(app, siteKey, grecaptcha, divId, initialized);
+ }
+ return initialized.promise;
+}
+function initializeEnterprise(app, siteKey) {
+ const initialized = new util.Deferred();
+ const state = getStateReference(app);
+ state.reCAPTCHAState = { initialized };
+ const divId = makeDiv(app);
+ const grecaptcha = getRecaptcha(true);
+ if (!grecaptcha) {
+ loadReCAPTCHAEnterpriseScript(() => {
+ const grecaptcha = getRecaptcha(true);
+ if (!grecaptcha) {
+ // it shouldn't happen.
+ throw new Error('no recaptcha');
+ }
+ queueWidgetRender(app, siteKey, grecaptcha, divId, initialized);
+ });
+ }
+ else {
+ queueWidgetRender(app, siteKey, grecaptcha, divId, initialized);
+ }
+ return initialized.promise;
+}
+/**
+ * Add listener to render the widget and resolve the promise when
+ * the grecaptcha.ready() event fires.
+ */
+function queueWidgetRender(app, siteKey, grecaptcha, container, initialized) {
+ grecaptcha.ready(() => {
+ // Invisible widgets allow us to set a different siteKey for each widget,
+ // so we use them to support multiple apps
+ renderInvisibleWidget(app, siteKey, grecaptcha, container);
+ initialized.resolve(grecaptcha);
+ });
+}
+/**
+ * Add invisible div to page.
+ */
+function makeDiv(app) {
+ const divId = `fire_app_check_${app.name}`;
+ const invisibleDiv = document.createElement('div');
+ invisibleDiv.id = divId;
+ invisibleDiv.style.display = 'none';
+ document.body.appendChild(invisibleDiv);
+ return divId;
+}
+async function getToken$1(app) {
+ ensureActivated(app);
+ // ensureActivated() guarantees that reCAPTCHAState is set
+ const reCAPTCHAState = getStateReference(app).reCAPTCHAState;
+ const recaptcha = await reCAPTCHAState.initialized.promise;
+ return new Promise((resolve, _reject) => {
+ // Updated after initialization is complete.
+ const reCAPTCHAState = getStateReference(app).reCAPTCHAState;
+ recaptcha.ready(() => {
+ resolve(
+ // widgetId is guaranteed to be available if reCAPTCHAState.initialized.promise resolved.
+ recaptcha.execute(reCAPTCHAState.widgetId, {
+ action: 'fire_app_check'
+ }));
+ });
+ });
+}
+/**
+ *
+ * @param app
+ * @param container - Id of a HTML element.
+ */
+function renderInvisibleWidget(app, siteKey, grecaptcha, container) {
+ const widgetId = grecaptcha.render(container, {
+ sitekey: siteKey,
+ size: 'invisible',
+ // Success callback - set state
+ callback: () => {
+ getStateReference(app).reCAPTCHAState.succeeded = true;
+ },
+ // Failure callback - set state
+ 'error-callback': () => {
+ getStateReference(app).reCAPTCHAState.succeeded = false;
+ }
+ });
+ const state = getStateReference(app);
+ state.reCAPTCHAState = {
+ ...state.reCAPTCHAState, // state.reCAPTCHAState is set in the initialize()
+ widgetId
+ };
+}
+function loadReCAPTCHAV3Script(onload) {
+ const script = document.createElement('script');
+ script.src = RECAPTCHA_URL;
+ script.onload = onload;
+ document.head.appendChild(script);
+}
+function loadReCAPTCHAEnterpriseScript(onload) {
+ const script = document.createElement('script');
+ script.src = RECAPTCHA_ENTERPRISE_URL;
+ script.onload = onload;
+ document.head.appendChild(script);
+}
+
+/**
+ * @license
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * App Check provider that can obtain a reCAPTCHA V3 token and exchange it
+ * for an App Check token.
+ *
+ * @public
+ */
+class ReCaptchaV3Provider {
+ /**
+ * Create a ReCaptchaV3Provider instance.
+ * @param siteKey - ReCAPTCHA V3 siteKey.
+ */
+ constructor(_siteKey) {
+ this._siteKey = _siteKey;
+ /**
+ * Throttle requests on certain error codes to prevent too many retries
+ * in a short time.
+ */
+ this._throttleData = null;
+ }
+ /**
+ * Returns an App Check token.
+ * @internal
+ */
+ async getToken() {
+ throwIfThrottled(this._throttleData);
+ // Top-level `getToken()` has already checked that App Check is initialized
+ // and therefore this._app and this._heartbeatServiceProvider are available.
+ const attestedClaimsToken = await getToken$1(this._app).catch(_e => {
+ // reCaptcha.execute() throws null which is not very descriptive.
+ throw ERROR_FACTORY.create("recaptcha-error" /* AppCheckError.RECAPTCHA_ERROR */);
+ });
+ // Check if a failure state was set by the recaptcha "error-callback".
+ if (!getStateReference(this._app).reCAPTCHAState?.succeeded) {
+ throw ERROR_FACTORY.create("recaptcha-error" /* AppCheckError.RECAPTCHA_ERROR */);
+ }
+ let result;
+ try {
+ result = await exchangeToken(getExchangeRecaptchaV3TokenRequest(this._app, attestedClaimsToken), this._heartbeatServiceProvider);
+ }
+ catch (e) {
+ if (e.code?.includes("fetch-status-error" /* AppCheckError.FETCH_STATUS_ERROR */)) {
+ this._throttleData = setBackoff(Number(e.customData?.httpStatus), this._throttleData);
+ throw ERROR_FACTORY.create("initial-throttle" /* AppCheckError.INITIAL_THROTTLE */, {
+ time: getDurationString(this._throttleData.allowRequestsAfter - Date.now()),
+ httpStatus: this._throttleData.httpStatus
+ });
+ }
+ else {
+ throw e;
+ }
+ }
+ // If successful, clear throttle data.
+ this._throttleData = null;
+ return result;
+ }
+ /**
+ * @internal
+ */
+ initialize(app$1) {
+ this._app = app$1;
+ this._heartbeatServiceProvider = app._getProvider(app$1, 'heartbeat');
+ initializeV3(app$1, this._siteKey).catch(() => {
+ /* we don't care about the initialization result */
+ });
+ }
+ /**
+ * @internal
+ */
+ isEqual(otherProvider) {
+ if (otherProvider instanceof ReCaptchaV3Provider) {
+ return this._siteKey === otherProvider._siteKey;
+ }
+ else {
+ return false;
+ }
+ }
+}
+/**
+ * App Check provider that can obtain a reCAPTCHA Enterprise token and exchange it
+ * for an App Check token.
+ *
+ * @public
+ */
+class ReCaptchaEnterpriseProvider {
+ /**
+ * Create a ReCaptchaEnterpriseProvider instance.
+ * @param siteKey - reCAPTCHA Enterprise score-based site key.
+ */
+ constructor(_siteKey) {
+ this._siteKey = _siteKey;
+ /**
+ * Throttle requests on certain error codes to prevent too many retries
+ * in a short time.
+ */
+ this._throttleData = null;
+ }
+ /**
+ * Returns an App Check token.
+ * @internal
+ */
+ async getToken() {
+ throwIfThrottled(this._throttleData);
+ // Top-level `getToken()` has already checked that App Check is initialized
+ // and therefore this._app and this._heartbeatServiceProvider are available.
+ const attestedClaimsToken = await getToken$1(this._app).catch(_e => {
+ // reCaptcha.execute() throws null which is not very descriptive.
+ throw ERROR_FACTORY.create("recaptcha-error" /* AppCheckError.RECAPTCHA_ERROR */);
+ });
+ // Check if a failure state was set by the recaptcha "error-callback".
+ if (!getStateReference(this._app).reCAPTCHAState?.succeeded) {
+ throw ERROR_FACTORY.create("recaptcha-error" /* AppCheckError.RECAPTCHA_ERROR */);
+ }
+ let result;
+ try {
+ result = await exchangeToken(getExchangeRecaptchaEnterpriseTokenRequest(this._app, attestedClaimsToken), this._heartbeatServiceProvider);
+ }
+ catch (e) {
+ if (e.code?.includes("fetch-status-error" /* AppCheckError.FETCH_STATUS_ERROR */)) {
+ this._throttleData = setBackoff(Number(e.customData?.httpStatus), this._throttleData);
+ throw ERROR_FACTORY.create("initial-throttle" /* AppCheckError.INITIAL_THROTTLE */, {
+ time: getDurationString(this._throttleData.allowRequestsAfter - Date.now()),
+ httpStatus: this._throttleData.httpStatus
+ });
+ }
+ else {
+ throw e;
+ }
+ }
+ // If successful, clear throttle data.
+ this._throttleData = null;
+ return result;
+ }
+ /**
+ * @internal
+ */
+ initialize(app$1) {
+ this._app = app$1;
+ this._heartbeatServiceProvider = app._getProvider(app$1, 'heartbeat');
+ initializeEnterprise(app$1, this._siteKey).catch(() => {
+ /* we don't care about the initialization result */
+ });
+ }
+ /**
+ * @internal
+ */
+ isEqual(otherProvider) {
+ if (otherProvider instanceof ReCaptchaEnterpriseProvider) {
+ return this._siteKey === otherProvider._siteKey;
+ }
+ else {
+ return false;
+ }
+ }
+}
+/**
+ * Custom provider class.
+ * @public
+ */
+class CustomProvider {
+ constructor(_customProviderOptions) {
+ this._customProviderOptions = _customProviderOptions;
+ }
+ /**
+ * @internal
+ */
+ async getToken() {
+ // custom provider
+ const customToken = await this._customProviderOptions.getToken();
+ // Try to extract IAT from custom token, in case this token is not
+ // being newly issued. JWT timestamps are in seconds since epoch.
+ const issuedAtTimeSeconds = util.issuedAtTime(customToken.token);
+ // Very basic validation, use current timestamp as IAT if JWT
+ // has no `iat` field or value is out of bounds.
+ const issuedAtTimeMillis = issuedAtTimeSeconds !== null &&
+ issuedAtTimeSeconds < Date.now() &&
+ issuedAtTimeSeconds > 0
+ ? issuedAtTimeSeconds * 1000
+ : Date.now();
+ return { ...customToken, issuedAtTimeMillis };
+ }
+ /**
+ * @internal
+ */
+ initialize(app) {
+ this._app = app;
+ }
+ /**
+ * @internal
+ */
+ isEqual(otherProvider) {
+ if (otherProvider instanceof CustomProvider) {
+ return (this._customProviderOptions.getToken.toString() ===
+ otherProvider._customProviderOptions.getToken.toString());
+ }
+ else {
+ return false;
+ }
+ }
+}
+/**
+ * Set throttle data to block requests until after a certain time
+ * depending on the failed request's status code.
+ * @param httpStatus - Status code of failed request.
+ * @param throttleData - `ThrottleData` object containing previous throttle
+ * data state.
+ * @returns Data about current throttle state and expiration time.
+ */
+function setBackoff(httpStatus, throttleData) {
+ /**
+ * Block retries for 1 day for the following error codes:
+ *
+ * 404: Likely malformed URL.
+ *
+ * 403:
+ * - Attestation failed
+ * - Wrong API key
+ * - Project deleted
+ */
+ if (httpStatus === 404 || httpStatus === 403) {
+ return {
+ backoffCount: 1,
+ allowRequestsAfter: Date.now() + ONE_DAY,
+ httpStatus
+ };
+ }
+ else {
+ /**
+ * For all other error codes, the time when it is ok to retry again
+ * is based on exponential backoff.
+ */
+ const backoffCount = throttleData ? throttleData.backoffCount : 0;
+ const backoffMillis = util.calculateBackoffMillis(backoffCount, 1000, 2);
+ return {
+ backoffCount: backoffCount + 1,
+ allowRequestsAfter: Date.now() + backoffMillis,
+ httpStatus
+ };
+ }
+}
+function throwIfThrottled(throttleData) {
+ if (throttleData) {
+ if (Date.now() - throttleData.allowRequestsAfter <= 0) {
+ // If before, throw.
+ throw ERROR_FACTORY.create("throttled" /* AppCheckError.THROTTLED */, {
+ time: getDurationString(throttleData.allowRequestsAfter - Date.now()),
+ httpStatus: throttleData.httpStatus
+ });
+ }
+ }
+}
+
+/**
+ * @license
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Activate App Check for the given app. Can be called only once per app.
+ * @param app - the {@link @firebase/app#FirebaseApp} to activate App Check for
+ * @param options - App Check initialization options
+ * @public
+ */
+function initializeAppCheck(app$1 = app.getApp(), options) {
+ app$1 = util.getModularInstance(app$1);
+ const provider = app._getProvider(app$1, 'app-check');
+ // Ensure initializeDebugMode() is only called once.
+ if (!getDebugState().initialized) {
+ initializeDebugMode();
+ }
+ // Log a message containing the debug token when `initializeAppCheck()`
+ // is called in debug mode.
+ if (isDebugMode()) {
+ // Do not block initialization to get the token for the message.
+ void getDebugToken().then(token =>
+ // Not using logger because I don't think we ever want this accidentally hidden.
+ console.log(`App Check debug token: ${token}. You will need to add it to your app's App Check settings in the Firebase console for it to work.`));
+ }
+ if (provider.isInitialized()) {
+ const existingInstance = provider.getImmediate();
+ const initialOptions = provider.getOptions();
+ if (initialOptions.isTokenAutoRefreshEnabled ===
+ options.isTokenAutoRefreshEnabled &&
+ initialOptions.provider.isEqual(options.provider)) {
+ return existingInstance;
+ }
+ else {
+ throw ERROR_FACTORY.create("already-initialized" /* AppCheckError.ALREADY_INITIALIZED */, {
+ appName: app$1.name
+ });
+ }
+ }
+ const appCheck = provider.initialize({ options });
+ _activate(app$1, options.provider, options.isTokenAutoRefreshEnabled);
+ // If isTokenAutoRefreshEnabled is false, do not send any requests to the
+ // exchange endpoint without an explicit call from the user either directly
+ // or through another Firebase library (storage, functions, etc.)
+ if (getStateReference(app$1).isTokenAutoRefreshEnabled) {
+ // Adding a listener will start the refresher and fetch a token if needed.
+ // This gets a token ready and prevents a delay when an internal library
+ // requests the token.
+ // Listener function does not need to do anything, its base functionality
+ // of calling getToken() already fetches token and writes it to memory/storage.
+ addTokenListener(appCheck, "INTERNAL" /* ListenerType.INTERNAL */, () => { });
+ }
+ return appCheck;
+}
+/**
+ * Activate App Check
+ * @param app - Firebase app to activate App Check for.
+ * @param provider - reCAPTCHA v3 provider or
+ * custom token provider.
+ * @param isTokenAutoRefreshEnabled - If true, the SDK automatically
+ * refreshes App Check tokens as needed. If undefined, defaults to the
+ * value of `app.automaticDataCollectionEnabled`, which defaults to
+ * false and can be set in the app config.
+ */
+function _activate(app, provider, isTokenAutoRefreshEnabled = false) {
+ // Create an entry in the APP_CHECK_STATES map. Further changes should
+ // directly mutate this object.
+ const state = setInitialState(app, { ...DEFAULT_STATE });
+ state.activated = true;
+ state.provider = provider; // Read cached token from storage if it exists and store it in memory.
+ state.cachedTokenPromise = readTokenFromStorage(app).then(cachedToken => {
+ if (cachedToken && isValid(cachedToken)) {
+ state.token = cachedToken;
+ // notify all listeners with the cached token
+ notifyTokenListeners(app, { token: cachedToken.token });
+ }
+ return cachedToken;
+ });
+ // Global `automaticDataCollectionEnabled` (defaults to true) and
+ // `isTokenAutoRefreshEnabled` must both be true.
+ state.isTokenAutoRefreshEnabled =
+ isTokenAutoRefreshEnabled && app.automaticDataCollectionEnabled;
+ if (!app.automaticDataCollectionEnabled && isTokenAutoRefreshEnabled) {
+ logger.warn('`isTokenAutoRefreshEnabled` is true but ' +
+ '`automaticDataCollectionEnabled` was set to false during' +
+ ' `initializeApp()`. This blocks automatic token refresh.');
+ }
+ state.provider.initialize(app);
+}
+/**
+ * Set whether App Check will automatically refresh tokens as needed.
+ *
+ * @param appCheckInstance - The App Check service instance.
+ * @param isTokenAutoRefreshEnabled - If true, the SDK automatically
+ * refreshes App Check tokens as needed. This overrides any value set
+ * during `initializeAppCheck()`.
+ * @public
+ */
+function setTokenAutoRefreshEnabled(appCheckInstance, isTokenAutoRefreshEnabled) {
+ const app = appCheckInstance.app;
+ const state = getStateReference(app);
+ // This will exist if any product libraries have called
+ // `addTokenListener()`
+ if (state.tokenRefresher) {
+ if (isTokenAutoRefreshEnabled === true) {
+ state.tokenRefresher.start();
+ }
+ else {
+ state.tokenRefresher.stop();
+ }
+ }
+ state.isTokenAutoRefreshEnabled = isTokenAutoRefreshEnabled;
+}
+/**
+ * Get the current App Check token. If `forceRefresh` is false, this function first
+ * checks for a valid token in memory, then local persistence (IndexedDB).
+ * If not found, or if `forceRefresh` is true, it makes a request to the
+ * App Check endpoint for a fresh token. That request attaches
+ * to the most recent in-flight request if one is present.
+ *
+ * @param appCheckInstance - The App Check service instance.
+ * @param forceRefresh - If true, will always try to fetch a fresh token.
+ * If false, will use a cached token if found in storage.
+ * @public
+ */
+async function getToken(appCheckInstance, forceRefresh) {
+ const result = await getToken$2(appCheckInstance, forceRefresh);
+ if (result.error) {
+ throw result.error;
+ }
+ if (result.internalError) {
+ throw result.internalError;
+ }
+ return { token: result.token };
+}
+/**
+ * Requests a Firebase App Check token. This method should be used
+ * only if you need to authorize requests to a non-Firebase backend.
+ *
+ * Returns limited-use tokens that are intended for use with your
+ * non-Firebase backend endpoints that are protected with
+ * <a href="https://firebase.google.com/docs/app-check/custom-resource-backend#replay-protection">
+ * Replay Protection</a>. This method
+ * does not affect the token generation behavior of the
+ * #getAppCheckToken() method.
+ *
+ * @param appCheckInstance - The App Check service instance.
+ * @returns The limited use token.
+ * @public
+ */
+function getLimitedUseToken(appCheckInstance) {
+ return getLimitedUseToken$1(appCheckInstance);
+}
+/**
+ * Wraps `addTokenListener`/`removeTokenListener` methods in an `Observer`
+ * pattern for public use.
+ */
+function onTokenChanged(appCheckInstance, onNextOrObserver, onError,
+/**
+ * NOTE: Although an `onCompletion` callback can be provided, it will
+ * never be called because the token stream is never-ending.
+ * It is added only for API consistency with the observer pattern, which
+ * we follow in JS APIs.
+ */
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+onCompletion) {
+ let nextFn = () => { };
+ let errorFn = () => { };
+ if (onNextOrObserver.next != null) {
+ nextFn = onNextOrObserver.next.bind(onNextOrObserver);
+ }
+ else {
+ nextFn = onNextOrObserver;
+ }
+ if (onNextOrObserver.error != null) {
+ errorFn = onNextOrObserver.error.bind(onNextOrObserver);
+ }
+ else if (onError) {
+ errorFn = onError;
+ }
+ addTokenListener(appCheckInstance, "EXTERNAL" /* ListenerType.EXTERNAL */, nextFn, errorFn);
+ return () => removeTokenListener(appCheckInstance.app, nextFn);
+}
+
+/**
+ * The Firebase App Check Web SDK.
+ *
+ * @remarks
+ * Firebase App Check does not work in a Node.js environment using `ReCaptchaV3Provider` or
+ * `ReCaptchaEnterpriseProvider`, but can be used in Node.js if you use
+ * `CustomProvider` and write your own attestation method.
+ *
+ * @packageDocumentation
+ */
+const APP_CHECK_NAME = 'app-check';
+const APP_CHECK_NAME_INTERNAL = 'app-check-internal';
+function registerAppCheck() {
+ // The public interface
+ app._registerComponent(new component.Component(APP_CHECK_NAME, container => {
+ // getImmediate for FirebaseApp will always succeed
+ const app = container.getProvider('app').getImmediate();
+ const heartbeatServiceProvider = container.getProvider('heartbeat');
+ return factory(app, heartbeatServiceProvider);
+ }, "PUBLIC" /* ComponentType.PUBLIC */)
+ .setInstantiationMode("EXPLICIT" /* InstantiationMode.EXPLICIT */)
+ /**
+ * Initialize app-check-internal after app-check is initialized to make AppCheck available to
+ * other Firebase SDKs
+ */
+ .setInstanceCreatedCallback((container, _identifier, _appcheckService) => {
+ container.getProvider(APP_CHECK_NAME_INTERNAL).initialize();
+ }));
+ // The internal interface used by other Firebase products
+ app._registerComponent(new component.Component(APP_CHECK_NAME_INTERNAL, container => {
+ const appCheck = container.getProvider('app-check').getImmediate();
+ return internalFactory(appCheck);
+ }, "PUBLIC" /* ComponentType.PUBLIC */).setInstantiationMode("EXPLICIT" /* InstantiationMode.EXPLICIT */));
+ app.registerVersion(name, version);
+}
+registerAppCheck();
+
+exports.CustomProvider = CustomProvider;
+exports.ReCaptchaEnterpriseProvider = ReCaptchaEnterpriseProvider;
+exports.ReCaptchaV3Provider = ReCaptchaV3Provider;
+exports.getLimitedUseToken = getLimitedUseToken;
+exports.getToken = getToken;
+exports.initializeAppCheck = initializeAppCheck;
+exports.onTokenChanged = onTokenChanged;
+exports.setTokenAutoRefreshEnabled = setTokenAutoRefreshEnabled;
+//# sourceMappingURL=index.cjs.js.map