@firebase/app-check
Advanced tools
Comparing version 0.2.1-canary.2493c171e to 0.2.1-canary.8599d9141
@@ -98,134 +98,2 @@ 'use strict'; | ||
*/ | ||
function getRecaptcha() { | ||
return self.grecaptcha; | ||
} | ||
function ensureActivated(app) { | ||
if (!getState(app).activated) { | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: app.name | ||
}); | ||
} | ||
} | ||
/** | ||
* Copied from https://stackoverflow.com/a/2117523 | ||
*/ | ||
function uuidv4() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
/** | ||
* @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. | ||
*/ | ||
var RECAPTCHA_URL = 'https://www.google.com/recaptcha/api.js'; | ||
function initialize(app, siteKey) { | ||
var state = getState(app); | ||
var initialized = new util.Deferred(); | ||
setState(app, tslib.__assign(tslib.__assign({}, state), { reCAPTCHAState: { initialized: initialized } })); | ||
var divId = "fire_app_check_" + app.name; | ||
var invisibleDiv = document.createElement('div'); | ||
invisibleDiv.id = divId; | ||
invisibleDiv.style.display = 'none'; | ||
document.body.appendChild(invisibleDiv); | ||
var grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
loadReCAPTCHAScript(function () { | ||
var grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
// it shouldn't happen. | ||
throw new Error('no recaptcha'); | ||
} | ||
grecaptcha.ready(function () { | ||
// Invisible widgets allow us to set a different siteKey for each widget, so we use them to support multiple apps | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
}); | ||
} | ||
else { | ||
grecaptcha.ready(function () { | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
} | ||
return initialized.promise; | ||
} | ||
function getToken$2(app) { | ||
return tslib.__awaiter(this, void 0, void 0, function () { | ||
var reCAPTCHAState, recaptcha; | ||
return tslib.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
ensureActivated(app); | ||
reCAPTCHAState = getState(app).reCAPTCHAState; | ||
return [4 /*yield*/, reCAPTCHAState.initialized.promise]; | ||
case 1: | ||
recaptcha = _a.sent(); | ||
return [2 /*return*/, new Promise(function (resolve, _reject) { | ||
// Updated after initialization is complete. | ||
var reCAPTCHAState = getState(app).reCAPTCHAState; | ||
recaptcha.ready(function () { | ||
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) { | ||
var widgetId = grecaptcha.render(container, { | ||
sitekey: siteKey, | ||
size: 'invisible' | ||
}); | ||
var state = getState(app); | ||
setState(app, tslib.__assign(tslib.__assign({}, state), { reCAPTCHAState: tslib.__assign(tslib.__assign({}, state.reCAPTCHAState), { // state.reCAPTCHAState is set in the initialize() | ||
widgetId: widgetId }) })); | ||
} | ||
function loadReCAPTCHAScript(onload) { | ||
var script = document.createElement('script'); | ||
script.src = "" + RECAPTCHA_URL; | ||
script.onload = onload; | ||
document.head.appendChild(script); | ||
} | ||
/** | ||
* @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. | ||
*/ | ||
var BASE_ENDPOINT = 'https://content-firebaseappcheck.googleapis.com/v1beta'; | ||
@@ -396,2 +264,47 @@ var EXCHANGE_RECAPTCHA_TOKEN_METHOD = 'exchangeRecaptchaToken'; | ||
*/ | ||
function getRecaptcha() { | ||
return self.grecaptcha; | ||
} | ||
function ensureActivated(app) { | ||
if (!getState(app).activated) { | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: app.name | ||
}); | ||
} | ||
} | ||
/** | ||
* Copied from https://stackoverflow.com/a/2117523 | ||
*/ | ||
function uuidv4() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
/** | ||
* 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); | ||
} | ||
/** | ||
* @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 exchangeToken(_a, platformLoggerProvider) { | ||
@@ -814,11 +727,2 @@ var url = _a.url, body = _a.body; | ||
/** | ||
* 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 will always resolve. | ||
@@ -828,6 +732,6 @@ * The result will contain an error field if there is any error. | ||
*/ | ||
function getToken$1(app, platformLoggerProvider, forceRefresh) { | ||
function getToken$2(app, platformLoggerProvider, forceRefresh) { | ||
if (forceRefresh === void 0) { forceRefresh = false; } | ||
return tslib.__awaiter(this, void 0, void 0, function () { | ||
var state, token, error, cachedToken, tokenFromDebugExchange, _a, _b, _c, customToken, issuedAtTimeSeconds, issuedAtTimeMillis, attestedClaimsToken, e_1, interopTokenResult; | ||
var state, token, error, cachedToken, tokenFromDebugExchange, _a, _b, _c, e_1, interopTokenResult; | ||
return tslib.__generator(this, function (_d) { | ||
@@ -841,3 +745,3 @@ switch (_d.label) { | ||
if (!!token) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, readTokenFromStorage(app)]; | ||
return [4 /*yield*/, state.cachedTokenPromise]; | ||
case 1: | ||
@@ -876,27 +780,11 @@ cachedToken = _d.sent(); | ||
case 6: | ||
_d.trys.push([6, 12, , 13]); | ||
if (!state.customProvider) return [3 /*break*/, 8]; | ||
return [4 /*yield*/, state.customProvider.getToken()]; | ||
_d.trys.push([6, 8, , 9]); | ||
return [4 /*yield*/, state.provider.getToken()]; | ||
case 7: | ||
customToken = _d.sent(); | ||
issuedAtTimeSeconds = util.issuedAtTime(customToken.token); | ||
issuedAtTimeMillis = issuedAtTimeSeconds !== null && | ||
issuedAtTimeSeconds < Date.now() && | ||
issuedAtTimeSeconds > 0 | ||
? issuedAtTimeSeconds * 1000 | ||
: Date.now(); | ||
token = tslib.__assign(tslib.__assign({}, customToken), { issuedAtTimeMillis: issuedAtTimeMillis }); | ||
return [3 /*break*/, 11]; | ||
case 8: return [4 /*yield*/, getToken$2(app).catch(function (_e) { | ||
// reCaptcha.execute() throws null which is not very descriptive. | ||
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */); | ||
})]; | ||
case 9: | ||
attestedClaimsToken = _d.sent(); | ||
return [4 /*yield*/, exchangeToken(getExchangeRecaptchaTokenRequest(app, attestedClaimsToken), platformLoggerProvider)]; | ||
case 10: | ||
// state.provider is populated in initializeAppCheck() | ||
// ensureActivated() at the top of this function checks that | ||
// initializeAppCheck() has been called. | ||
token = _d.sent(); | ||
_d.label = 11; | ||
case 11: return [3 /*break*/, 13]; | ||
case 12: | ||
return [3 /*break*/, 9]; | ||
case 8: | ||
e_1 = _d.sent(); | ||
@@ -906,10 +794,10 @@ // `getToken()` should never throw, but logging error text to console will aid debugging. | ||
error = e_1; | ||
return [3 /*break*/, 13]; | ||
case 13: | ||
if (!!token) return [3 /*break*/, 14]; | ||
return [3 /*break*/, 9]; | ||
case 9: | ||
if (!!token) return [3 /*break*/, 10]; | ||
// if token is undefined, there must be an error. | ||
// we return a dummy token along with the error | ||
interopTokenResult = makeDummyTokenResult(error); | ||
return [3 /*break*/, 16]; | ||
case 14: | ||
return [3 /*break*/, 12]; | ||
case 10: | ||
interopTokenResult = { | ||
@@ -922,6 +810,6 @@ token: token.token | ||
return [4 /*yield*/, writeTokenToStorage(app, token)]; | ||
case 15: | ||
case 11: | ||
_d.sent(); | ||
_d.label = 16; | ||
case 16: | ||
_d.label = 12; | ||
case 12: | ||
notifyTokenListeners(app, interopTokenResult); | ||
@@ -953,3 +841,4 @@ return [2 /*return*/, interopTokenResult]; | ||
} | ||
// invoke the listener async immediately if there is a valid token | ||
// Invoke the listener async immediately if there is a valid token | ||
// in memory. | ||
if (state.token && isValid(state.token)) { | ||
@@ -963,2 +852,16 @@ var validToken_1 = state.token; | ||
} | ||
else if (state.token == null) { | ||
// Only check cache if there was no token. If the token was invalid, | ||
// skip this and rely on exchange endpoint. | ||
void state | ||
.cachedTokenPromise // Storage token promise. Always populated in `activate()`. | ||
.then(function (cachedToken) { | ||
if (cachedToken && isValid(cachedToken)) { | ||
listener({ token: cachedToken.token }); | ||
} | ||
}) | ||
.catch(function () { | ||
/** Ignore errors in listeners. */ | ||
}); | ||
} | ||
setState(app, newState); | ||
@@ -988,7 +891,7 @@ } | ||
if (!!state.token) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, getToken$1(app, platformLoggerProvider)]; | ||
return [4 /*yield*/, getToken$2(app, platformLoggerProvider)]; | ||
case 1: | ||
result = _a.sent(); | ||
return [3 /*break*/, 4]; | ||
case 2: return [4 /*yield*/, getToken$1(app, platformLoggerProvider, true)]; | ||
case 2: return [4 /*yield*/, getToken$2(app, platformLoggerProvider, true)]; | ||
case 3: | ||
@@ -1075,11 +978,225 @@ result = _a.sent(); | ||
*/ | ||
var RECAPTCHA_URL = 'https://www.google.com/recaptcha/api.js'; | ||
function initialize(app, siteKey) { | ||
var state = getState(app); | ||
var initialized = new util.Deferred(); | ||
setState(app, tslib.__assign(tslib.__assign({}, state), { reCAPTCHAState: { initialized: initialized } })); | ||
var divId = "fire_app_check_" + app.name; | ||
var invisibleDiv = document.createElement('div'); | ||
invisibleDiv.id = divId; | ||
invisibleDiv.style.display = 'none'; | ||
document.body.appendChild(invisibleDiv); | ||
var grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
loadReCAPTCHAScript(function () { | ||
var grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
// it shouldn't happen. | ||
throw new Error('no recaptcha'); | ||
} | ||
grecaptcha.ready(function () { | ||
// Invisible widgets allow us to set a different siteKey for each widget, so we use them to support multiple apps | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
}); | ||
} | ||
else { | ||
grecaptcha.ready(function () { | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
} | ||
return initialized.promise; | ||
} | ||
function getToken$1(app) { | ||
return tslib.__awaiter(this, void 0, void 0, function () { | ||
var reCAPTCHAState, recaptcha; | ||
return tslib.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
ensureActivated(app); | ||
reCAPTCHAState = getState(app).reCAPTCHAState; | ||
return [4 /*yield*/, reCAPTCHAState.initialized.promise]; | ||
case 1: | ||
recaptcha = _a.sent(); | ||
return [2 /*return*/, new Promise(function (resolve, _reject) { | ||
// Updated after initialization is complete. | ||
var reCAPTCHAState = getState(app).reCAPTCHAState; | ||
recaptcha.ready(function () { | ||
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) { | ||
var widgetId = grecaptcha.render(container, { | ||
sitekey: siteKey, | ||
size: 'invisible' | ||
}); | ||
var state = getState(app); | ||
setState(app, tslib.__assign(tslib.__assign({}, state), { reCAPTCHAState: tslib.__assign(tslib.__assign({}, state.reCAPTCHAState), { // state.reCAPTCHAState is set in the initialize() | ||
widgetId: widgetId }) })); | ||
} | ||
function loadReCAPTCHAScript(onload) { | ||
var script = document.createElement('script'); | ||
script.src = "" + RECAPTCHA_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. | ||
*/ | ||
var ReCaptchaV3Provider = /** @class */ (function () { | ||
/** | ||
* Create a ReCaptchaV3Provider instance. | ||
* @param siteKey - ReCAPTCHA V3 siteKey. | ||
*/ | ||
function ReCaptchaV3Provider(_siteKey) { | ||
this._siteKey = _siteKey; | ||
} | ||
/** | ||
* Returns an App Check token. | ||
* @internal | ||
*/ | ||
ReCaptchaV3Provider.prototype.getToken = function () { | ||
return tslib.__awaiter(this, void 0, void 0, function () { | ||
var attestedClaimsToken; | ||
return tslib.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!this._app || !this._platformLoggerProvider) { | ||
// This should only occur if user has not called initializeAppCheck(). | ||
// We don't have an appName to provide if so. | ||
// This should already be caught in the top level `getToken()` function. | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: '' | ||
}); | ||
} | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, getToken$1(this._app)]; | ||
case 2: | ||
attestedClaimsToken = _a.sent(); | ||
return [3 /*break*/, 4]; | ||
case 3: | ||
_a.sent(); | ||
// reCaptcha.execute() throws null which is not very descriptive. | ||
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */); | ||
case 4: return [2 /*return*/, exchangeToken(getExchangeRecaptchaTokenRequest(this._app, attestedClaimsToken), this._platformLoggerProvider)]; | ||
} | ||
}); | ||
}); | ||
}; | ||
ReCaptchaV3Provider.prototype.initialize = function (app, platformLoggerProvider) { | ||
this._app = app; | ||
this._platformLoggerProvider = platformLoggerProvider; | ||
initialize(app, this._siteKey).catch(function () { | ||
/* we don't care about the initialization result */ | ||
}); | ||
}; | ||
return ReCaptchaV3Provider; | ||
}()); | ||
/** | ||
* Custom provider class. | ||
*/ | ||
var CustomProvider = /** @class */ (function () { | ||
function CustomProvider(_customProviderOptions) { | ||
this._customProviderOptions = _customProviderOptions; | ||
} | ||
/** | ||
* @internal | ||
*/ | ||
CustomProvider.prototype.getToken = function () { | ||
return tslib.__awaiter(this, void 0, void 0, function () { | ||
var customToken, issuedAtTimeSeconds, issuedAtTimeMillis; | ||
return tslib.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!this._app) { | ||
// This should only occur if user has not called initializeAppCheck(). | ||
// We don't have an appName to provide if so. | ||
// This should already be caught in the top level `getToken()` function. | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: '' | ||
}); | ||
} | ||
return [4 /*yield*/, this._customProviderOptions.getToken()]; | ||
case 1: | ||
customToken = _a.sent(); | ||
issuedAtTimeSeconds = util.issuedAtTime(customToken.token); | ||
issuedAtTimeMillis = issuedAtTimeSeconds !== null && | ||
issuedAtTimeSeconds < Date.now() && | ||
issuedAtTimeSeconds > 0 | ||
? issuedAtTimeSeconds * 1000 | ||
: Date.now(); | ||
return [2 /*return*/, tslib.__assign(tslib.__assign({}, customToken), { issuedAtTimeMillis: issuedAtTimeMillis })]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* @internal | ||
*/ | ||
CustomProvider.prototype.initialize = function (app) { | ||
this._app = app; | ||
}; | ||
return CustomProvider; | ||
}()); | ||
/** | ||
* @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. | ||
*/ | ||
/** | ||
* | ||
* @param app | ||
* @param siteKeyOrProvider - optional custom attestation provider | ||
* or reCAPTCHA siteKey | ||
* or reCAPTCHA provider | ||
* @param isTokenAutoRefreshEnabled - if true, enables auto refresh | ||
* of appCheck token. | ||
*/ | ||
function activate(app, siteKeyOrProvider, isTokenAutoRefreshEnabled) { | ||
function activate(app, siteKeyOrProvider, platformLoggerProvider, isTokenAutoRefreshEnabled) { | ||
var state = getState(app); | ||
@@ -1092,7 +1209,23 @@ if (state.activated) { | ||
var newState = tslib.__assign(tslib.__assign({}, state), { activated: true }); | ||
// Read cached token from storage if it exists and store it in memory. | ||
newState.cachedTokenPromise = readTokenFromStorage(app).then(function (cachedToken) { | ||
if (cachedToken && isValid(cachedToken)) { | ||
setState(app, tslib.__assign(tslib.__assign({}, getState(app)), { token: cachedToken })); | ||
} | ||
return cachedToken; | ||
}); | ||
if (typeof siteKeyOrProvider === 'string') { | ||
newState.siteKey = siteKeyOrProvider; | ||
newState.provider = new ReCaptchaV3Provider(siteKeyOrProvider); | ||
} | ||
else if (siteKeyOrProvider instanceof ReCaptchaV3Provider || | ||
siteKeyOrProvider instanceof CustomProvider) { | ||
newState.provider = siteKeyOrProvider; | ||
} | ||
else { | ||
newState.customProvider = siteKeyOrProvider; | ||
// Process "old" custom provider to avoid breaking previous users. | ||
// This was defined at beta release as simply an object with a | ||
// getToken() method. | ||
newState.provider = new CustomProvider({ | ||
getToken: siteKeyOrProvider.getToken | ||
}); | ||
} | ||
@@ -1107,8 +1240,3 @@ // Use value of global `automaticDataCollectionEnabled` (which | ||
setState(app, newState); | ||
// initialize reCAPTCHA if siteKey is provided | ||
if (newState.siteKey) { | ||
initialize(app, newState.siteKey).catch(function () { | ||
/* we don't care about the initialization result in activate() */ | ||
}); | ||
} | ||
newState.provider.initialize(app, platformLoggerProvider); | ||
} | ||
@@ -1137,3 +1265,3 @@ function setTokenAutoRefreshEnabled(app, isTokenAutoRefreshEnabled) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getToken$1(app, platformLoggerProvider, forceRefresh)]; | ||
case 0: return [4 /*yield*/, getToken$2(app, platformLoggerProvider, forceRefresh)]; | ||
case 1: | ||
@@ -1195,3 +1323,5 @@ result = _a.sent(); | ||
app: app, | ||
activate: function (siteKeyOrProvider, isTokenAutoRefreshEnabled) { return activate(app, siteKeyOrProvider, isTokenAutoRefreshEnabled); }, | ||
activate: function (siteKeyOrProvider, isTokenAutoRefreshEnabled) { | ||
return activate(app, siteKeyOrProvider, platformLoggerProvider, isTokenAutoRefreshEnabled); | ||
}, | ||
setTokenAutoRefreshEnabled: function (isTokenAutoRefreshEnabled) { | ||
@@ -1227,3 +1357,3 @@ return setTokenAutoRefreshEnabled(app, isTokenAutoRefreshEnabled); | ||
getToken: function (forceRefresh) { | ||
return getToken$1(app, platformLoggerProvider, forceRefresh); | ||
return getToken$2(app, platformLoggerProvider, forceRefresh); | ||
}, | ||
@@ -1238,3 +1368,3 @@ addTokenListener: function (listener) { | ||
var name = "@firebase/app-check"; | ||
var version = "0.2.1-canary.2493c171e"; | ||
var version = "0.2.1-canary.8599d9141"; | ||
@@ -1267,2 +1397,6 @@ /** | ||
}, "PUBLIC" /* PUBLIC */) | ||
.setServiceProps({ | ||
ReCaptchaV3Provider: ReCaptchaV3Provider, | ||
CustomProvider: CustomProvider | ||
}) | ||
/** | ||
@@ -1269,0 +1403,0 @@ * AppCheck can only be initialized by explicitly calling firebase.appCheck() |
import firebase from '@firebase/app'; | ||
import { Component } from '@firebase/component'; | ||
import { __awaiter, __generator, __assign, __spreadArray } from 'tslib'; | ||
import { ErrorFactory, Deferred, isIndexedDBAvailable, getGlobal, base64, issuedAtTime } from '@firebase/util'; | ||
import { ErrorFactory, Deferred, base64, isIndexedDBAvailable, getGlobal, issuedAtTime } from '@firebase/util'; | ||
import { Logger } from '@firebase/logger'; | ||
@@ -92,134 +92,2 @@ | ||
*/ | ||
function getRecaptcha() { | ||
return self.grecaptcha; | ||
} | ||
function ensureActivated(app) { | ||
if (!getState(app).activated) { | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: app.name | ||
}); | ||
} | ||
} | ||
/** | ||
* Copied from https://stackoverflow.com/a/2117523 | ||
*/ | ||
function uuidv4() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
/** | ||
* @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. | ||
*/ | ||
var RECAPTCHA_URL = 'https://www.google.com/recaptcha/api.js'; | ||
function initialize(app, siteKey) { | ||
var state = getState(app); | ||
var initialized = new Deferred(); | ||
setState(app, __assign(__assign({}, state), { reCAPTCHAState: { initialized: initialized } })); | ||
var divId = "fire_app_check_" + app.name; | ||
var invisibleDiv = document.createElement('div'); | ||
invisibleDiv.id = divId; | ||
invisibleDiv.style.display = 'none'; | ||
document.body.appendChild(invisibleDiv); | ||
var grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
loadReCAPTCHAScript(function () { | ||
var grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
// it shouldn't happen. | ||
throw new Error('no recaptcha'); | ||
} | ||
grecaptcha.ready(function () { | ||
// Invisible widgets allow us to set a different siteKey for each widget, so we use them to support multiple apps | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
}); | ||
} | ||
else { | ||
grecaptcha.ready(function () { | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
} | ||
return initialized.promise; | ||
} | ||
function getToken$2(app) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var reCAPTCHAState, recaptcha; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
ensureActivated(app); | ||
reCAPTCHAState = getState(app).reCAPTCHAState; | ||
return [4 /*yield*/, reCAPTCHAState.initialized.promise]; | ||
case 1: | ||
recaptcha = _a.sent(); | ||
return [2 /*return*/, new Promise(function (resolve, _reject) { | ||
// Updated after initialization is complete. | ||
var reCAPTCHAState = getState(app).reCAPTCHAState; | ||
recaptcha.ready(function () { | ||
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) { | ||
var widgetId = grecaptcha.render(container, { | ||
sitekey: siteKey, | ||
size: 'invisible' | ||
}); | ||
var state = getState(app); | ||
setState(app, __assign(__assign({}, state), { reCAPTCHAState: __assign(__assign({}, state.reCAPTCHAState), { // state.reCAPTCHAState is set in the initialize() | ||
widgetId: widgetId }) })); | ||
} | ||
function loadReCAPTCHAScript(onload) { | ||
var script = document.createElement('script'); | ||
script.src = "" + RECAPTCHA_URL; | ||
script.onload = onload; | ||
document.head.appendChild(script); | ||
} | ||
/** | ||
* @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. | ||
*/ | ||
var BASE_ENDPOINT = 'https://content-firebaseappcheck.googleapis.com/v1beta'; | ||
@@ -390,2 +258,47 @@ var EXCHANGE_RECAPTCHA_TOKEN_METHOD = 'exchangeRecaptchaToken'; | ||
*/ | ||
function getRecaptcha() { | ||
return self.grecaptcha; | ||
} | ||
function ensureActivated(app) { | ||
if (!getState(app).activated) { | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: app.name | ||
}); | ||
} | ||
} | ||
/** | ||
* Copied from https://stackoverflow.com/a/2117523 | ||
*/ | ||
function uuidv4() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
/** | ||
* Stringify and base64 encode token error data. | ||
* | ||
* @param tokenError Error data, currently hardcoded. | ||
*/ | ||
function formatDummyToken(tokenErrorData) { | ||
return base64.encodeString(JSON.stringify(tokenErrorData), | ||
/* webSafe= */ false); | ||
} | ||
/** | ||
* @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 exchangeToken(_a, platformLoggerProvider) { | ||
@@ -808,11 +721,2 @@ var url = _a.url, body = _a.body; | ||
/** | ||
* Stringify and base64 encode token error data. | ||
* | ||
* @param tokenError Error data, currently hardcoded. | ||
*/ | ||
function formatDummyToken(tokenErrorData) { | ||
return base64.encodeString(JSON.stringify(tokenErrorData), | ||
/* webSafe= */ false); | ||
} | ||
/** | ||
* This function will always resolve. | ||
@@ -822,6 +726,6 @@ * The result will contain an error field if there is any error. | ||
*/ | ||
function getToken$1(app, platformLoggerProvider, forceRefresh) { | ||
function getToken$2(app, platformLoggerProvider, forceRefresh) { | ||
if (forceRefresh === void 0) { forceRefresh = false; } | ||
return __awaiter(this, void 0, void 0, function () { | ||
var state, token, error, cachedToken, tokenFromDebugExchange, _a, _b, _c, customToken, issuedAtTimeSeconds, issuedAtTimeMillis, attestedClaimsToken, e_1, interopTokenResult; | ||
var state, token, error, cachedToken, tokenFromDebugExchange, _a, _b, _c, e_1, interopTokenResult; | ||
return __generator(this, function (_d) { | ||
@@ -835,3 +739,3 @@ switch (_d.label) { | ||
if (!!token) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, readTokenFromStorage(app)]; | ||
return [4 /*yield*/, state.cachedTokenPromise]; | ||
case 1: | ||
@@ -870,27 +774,11 @@ cachedToken = _d.sent(); | ||
case 6: | ||
_d.trys.push([6, 12, , 13]); | ||
if (!state.customProvider) return [3 /*break*/, 8]; | ||
return [4 /*yield*/, state.customProvider.getToken()]; | ||
_d.trys.push([6, 8, , 9]); | ||
return [4 /*yield*/, state.provider.getToken()]; | ||
case 7: | ||
customToken = _d.sent(); | ||
issuedAtTimeSeconds = issuedAtTime(customToken.token); | ||
issuedAtTimeMillis = issuedAtTimeSeconds !== null && | ||
issuedAtTimeSeconds < Date.now() && | ||
issuedAtTimeSeconds > 0 | ||
? issuedAtTimeSeconds * 1000 | ||
: Date.now(); | ||
token = __assign(__assign({}, customToken), { issuedAtTimeMillis: issuedAtTimeMillis }); | ||
return [3 /*break*/, 11]; | ||
case 8: return [4 /*yield*/, getToken$2(app).catch(function (_e) { | ||
// reCaptcha.execute() throws null which is not very descriptive. | ||
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */); | ||
})]; | ||
case 9: | ||
attestedClaimsToken = _d.sent(); | ||
return [4 /*yield*/, exchangeToken(getExchangeRecaptchaTokenRequest(app, attestedClaimsToken), platformLoggerProvider)]; | ||
case 10: | ||
// state.provider is populated in initializeAppCheck() | ||
// ensureActivated() at the top of this function checks that | ||
// initializeAppCheck() has been called. | ||
token = _d.sent(); | ||
_d.label = 11; | ||
case 11: return [3 /*break*/, 13]; | ||
case 12: | ||
return [3 /*break*/, 9]; | ||
case 8: | ||
e_1 = _d.sent(); | ||
@@ -900,10 +788,10 @@ // `getToken()` should never throw, but logging error text to console will aid debugging. | ||
error = e_1; | ||
return [3 /*break*/, 13]; | ||
case 13: | ||
if (!!token) return [3 /*break*/, 14]; | ||
return [3 /*break*/, 9]; | ||
case 9: | ||
if (!!token) return [3 /*break*/, 10]; | ||
// if token is undefined, there must be an error. | ||
// we return a dummy token along with the error | ||
interopTokenResult = makeDummyTokenResult(error); | ||
return [3 /*break*/, 16]; | ||
case 14: | ||
return [3 /*break*/, 12]; | ||
case 10: | ||
interopTokenResult = { | ||
@@ -916,6 +804,6 @@ token: token.token | ||
return [4 /*yield*/, writeTokenToStorage(app, token)]; | ||
case 15: | ||
case 11: | ||
_d.sent(); | ||
_d.label = 16; | ||
case 16: | ||
_d.label = 12; | ||
case 12: | ||
notifyTokenListeners(app, interopTokenResult); | ||
@@ -947,3 +835,4 @@ return [2 /*return*/, interopTokenResult]; | ||
} | ||
// invoke the listener async immediately if there is a valid token | ||
// Invoke the listener async immediately if there is a valid token | ||
// in memory. | ||
if (state.token && isValid(state.token)) { | ||
@@ -957,2 +846,16 @@ var validToken_1 = state.token; | ||
} | ||
else if (state.token == null) { | ||
// Only check cache if there was no token. If the token was invalid, | ||
// skip this and rely on exchange endpoint. | ||
void state | ||
.cachedTokenPromise // Storage token promise. Always populated in `activate()`. | ||
.then(function (cachedToken) { | ||
if (cachedToken && isValid(cachedToken)) { | ||
listener({ token: cachedToken.token }); | ||
} | ||
}) | ||
.catch(function () { | ||
/** Ignore errors in listeners. */ | ||
}); | ||
} | ||
setState(app, newState); | ||
@@ -982,7 +885,7 @@ } | ||
if (!!state.token) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, getToken$1(app, platformLoggerProvider)]; | ||
return [4 /*yield*/, getToken$2(app, platformLoggerProvider)]; | ||
case 1: | ||
result = _a.sent(); | ||
return [3 /*break*/, 4]; | ||
case 2: return [4 /*yield*/, getToken$1(app, platformLoggerProvider, true)]; | ||
case 2: return [4 /*yield*/, getToken$2(app, platformLoggerProvider, true)]; | ||
case 3: | ||
@@ -1069,11 +972,225 @@ result = _a.sent(); | ||
*/ | ||
var RECAPTCHA_URL = 'https://www.google.com/recaptcha/api.js'; | ||
function initialize(app, siteKey) { | ||
var state = getState(app); | ||
var initialized = new Deferred(); | ||
setState(app, __assign(__assign({}, state), { reCAPTCHAState: { initialized: initialized } })); | ||
var divId = "fire_app_check_" + app.name; | ||
var invisibleDiv = document.createElement('div'); | ||
invisibleDiv.id = divId; | ||
invisibleDiv.style.display = 'none'; | ||
document.body.appendChild(invisibleDiv); | ||
var grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
loadReCAPTCHAScript(function () { | ||
var grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
// it shouldn't happen. | ||
throw new Error('no recaptcha'); | ||
} | ||
grecaptcha.ready(function () { | ||
// Invisible widgets allow us to set a different siteKey for each widget, so we use them to support multiple apps | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
}); | ||
} | ||
else { | ||
grecaptcha.ready(function () { | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
} | ||
return initialized.promise; | ||
} | ||
function getToken$1(app) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var reCAPTCHAState, recaptcha; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
ensureActivated(app); | ||
reCAPTCHAState = getState(app).reCAPTCHAState; | ||
return [4 /*yield*/, reCAPTCHAState.initialized.promise]; | ||
case 1: | ||
recaptcha = _a.sent(); | ||
return [2 /*return*/, new Promise(function (resolve, _reject) { | ||
// Updated after initialization is complete. | ||
var reCAPTCHAState = getState(app).reCAPTCHAState; | ||
recaptcha.ready(function () { | ||
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) { | ||
var widgetId = grecaptcha.render(container, { | ||
sitekey: siteKey, | ||
size: 'invisible' | ||
}); | ||
var state = getState(app); | ||
setState(app, __assign(__assign({}, state), { reCAPTCHAState: __assign(__assign({}, state.reCAPTCHAState), { // state.reCAPTCHAState is set in the initialize() | ||
widgetId: widgetId }) })); | ||
} | ||
function loadReCAPTCHAScript(onload) { | ||
var script = document.createElement('script'); | ||
script.src = "" + RECAPTCHA_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. | ||
*/ | ||
var ReCaptchaV3Provider = /** @class */ (function () { | ||
/** | ||
* Create a ReCaptchaV3Provider instance. | ||
* @param siteKey - ReCAPTCHA V3 siteKey. | ||
*/ | ||
function ReCaptchaV3Provider(_siteKey) { | ||
this._siteKey = _siteKey; | ||
} | ||
/** | ||
* Returns an App Check token. | ||
* @internal | ||
*/ | ||
ReCaptchaV3Provider.prototype.getToken = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var attestedClaimsToken; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!this._app || !this._platformLoggerProvider) { | ||
// This should only occur if user has not called initializeAppCheck(). | ||
// We don't have an appName to provide if so. | ||
// This should already be caught in the top level `getToken()` function. | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: '' | ||
}); | ||
} | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, getToken$1(this._app)]; | ||
case 2: | ||
attestedClaimsToken = _a.sent(); | ||
return [3 /*break*/, 4]; | ||
case 3: | ||
_a.sent(); | ||
// reCaptcha.execute() throws null which is not very descriptive. | ||
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */); | ||
case 4: return [2 /*return*/, exchangeToken(getExchangeRecaptchaTokenRequest(this._app, attestedClaimsToken), this._platformLoggerProvider)]; | ||
} | ||
}); | ||
}); | ||
}; | ||
ReCaptchaV3Provider.prototype.initialize = function (app, platformLoggerProvider) { | ||
this._app = app; | ||
this._platformLoggerProvider = platformLoggerProvider; | ||
initialize(app, this._siteKey).catch(function () { | ||
/* we don't care about the initialization result */ | ||
}); | ||
}; | ||
return ReCaptchaV3Provider; | ||
}()); | ||
/** | ||
* Custom provider class. | ||
*/ | ||
var CustomProvider = /** @class */ (function () { | ||
function CustomProvider(_customProviderOptions) { | ||
this._customProviderOptions = _customProviderOptions; | ||
} | ||
/** | ||
* @internal | ||
*/ | ||
CustomProvider.prototype.getToken = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var customToken, issuedAtTimeSeconds, issuedAtTimeMillis; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!this._app) { | ||
// This should only occur if user has not called initializeAppCheck(). | ||
// We don't have an appName to provide if so. | ||
// This should already be caught in the top level `getToken()` function. | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: '' | ||
}); | ||
} | ||
return [4 /*yield*/, this._customProviderOptions.getToken()]; | ||
case 1: | ||
customToken = _a.sent(); | ||
issuedAtTimeSeconds = issuedAtTime(customToken.token); | ||
issuedAtTimeMillis = issuedAtTimeSeconds !== null && | ||
issuedAtTimeSeconds < Date.now() && | ||
issuedAtTimeSeconds > 0 | ||
? issuedAtTimeSeconds * 1000 | ||
: Date.now(); | ||
return [2 /*return*/, __assign(__assign({}, customToken), { issuedAtTimeMillis: issuedAtTimeMillis })]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* @internal | ||
*/ | ||
CustomProvider.prototype.initialize = function (app) { | ||
this._app = app; | ||
}; | ||
return CustomProvider; | ||
}()); | ||
/** | ||
* @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. | ||
*/ | ||
/** | ||
* | ||
* @param app | ||
* @param siteKeyOrProvider - optional custom attestation provider | ||
* or reCAPTCHA siteKey | ||
* or reCAPTCHA provider | ||
* @param isTokenAutoRefreshEnabled - if true, enables auto refresh | ||
* of appCheck token. | ||
*/ | ||
function activate(app, siteKeyOrProvider, isTokenAutoRefreshEnabled) { | ||
function activate(app, siteKeyOrProvider, platformLoggerProvider, isTokenAutoRefreshEnabled) { | ||
var state = getState(app); | ||
@@ -1086,7 +1203,23 @@ if (state.activated) { | ||
var newState = __assign(__assign({}, state), { activated: true }); | ||
// Read cached token from storage if it exists and store it in memory. | ||
newState.cachedTokenPromise = readTokenFromStorage(app).then(function (cachedToken) { | ||
if (cachedToken && isValid(cachedToken)) { | ||
setState(app, __assign(__assign({}, getState(app)), { token: cachedToken })); | ||
} | ||
return cachedToken; | ||
}); | ||
if (typeof siteKeyOrProvider === 'string') { | ||
newState.siteKey = siteKeyOrProvider; | ||
newState.provider = new ReCaptchaV3Provider(siteKeyOrProvider); | ||
} | ||
else if (siteKeyOrProvider instanceof ReCaptchaV3Provider || | ||
siteKeyOrProvider instanceof CustomProvider) { | ||
newState.provider = siteKeyOrProvider; | ||
} | ||
else { | ||
newState.customProvider = siteKeyOrProvider; | ||
// Process "old" custom provider to avoid breaking previous users. | ||
// This was defined at beta release as simply an object with a | ||
// getToken() method. | ||
newState.provider = new CustomProvider({ | ||
getToken: siteKeyOrProvider.getToken | ||
}); | ||
} | ||
@@ -1101,8 +1234,3 @@ // Use value of global `automaticDataCollectionEnabled` (which | ||
setState(app, newState); | ||
// initialize reCAPTCHA if siteKey is provided | ||
if (newState.siteKey) { | ||
initialize(app, newState.siteKey).catch(function () { | ||
/* we don't care about the initialization result in activate() */ | ||
}); | ||
} | ||
newState.provider.initialize(app, platformLoggerProvider); | ||
} | ||
@@ -1131,3 +1259,3 @@ function setTokenAutoRefreshEnabled(app, isTokenAutoRefreshEnabled) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getToken$1(app, platformLoggerProvider, forceRefresh)]; | ||
case 0: return [4 /*yield*/, getToken$2(app, platformLoggerProvider, forceRefresh)]; | ||
case 1: | ||
@@ -1189,3 +1317,5 @@ result = _a.sent(); | ||
app: app, | ||
activate: function (siteKeyOrProvider, isTokenAutoRefreshEnabled) { return activate(app, siteKeyOrProvider, isTokenAutoRefreshEnabled); }, | ||
activate: function (siteKeyOrProvider, isTokenAutoRefreshEnabled) { | ||
return activate(app, siteKeyOrProvider, platformLoggerProvider, isTokenAutoRefreshEnabled); | ||
}, | ||
setTokenAutoRefreshEnabled: function (isTokenAutoRefreshEnabled) { | ||
@@ -1221,3 +1351,3 @@ return setTokenAutoRefreshEnabled(app, isTokenAutoRefreshEnabled); | ||
getToken: function (forceRefresh) { | ||
return getToken$1(app, platformLoggerProvider, forceRefresh); | ||
return getToken$2(app, platformLoggerProvider, forceRefresh); | ||
}, | ||
@@ -1232,3 +1362,3 @@ addTokenListener: function (listener) { | ||
var name = "@firebase/app-check"; | ||
var version = "0.2.1-canary.2493c171e"; | ||
var version = "0.2.1-canary.8599d9141"; | ||
@@ -1261,2 +1391,6 @@ /** | ||
}, "PUBLIC" /* PUBLIC */) | ||
.setServiceProps({ | ||
ReCaptchaV3Provider: ReCaptchaV3Provider, | ||
CustomProvider: CustomProvider | ||
}) | ||
/** | ||
@@ -1263,0 +1397,0 @@ * AppCheck can only be initialized by explicitly calling firebase.appCheck() |
import firebase from '@firebase/app'; | ||
import { Component } from '@firebase/component'; | ||
import { ErrorFactory, Deferred, isIndexedDBAvailable, getGlobal, issuedAtTime, base64 } from '@firebase/util'; | ||
import { ErrorFactory, Deferred, base64, isIndexedDBAvailable, getGlobal, issuedAtTime } from '@firebase/util'; | ||
import { Logger } from '@firebase/logger'; | ||
@@ -90,125 +90,2 @@ | ||
*/ | ||
function getRecaptcha() { | ||
return self.grecaptcha; | ||
} | ||
function ensureActivated(app) { | ||
if (!getState(app).activated) { | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: app.name | ||
}); | ||
} | ||
} | ||
/** | ||
* Copied from https://stackoverflow.com/a/2117523 | ||
*/ | ||
function uuidv4() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { | ||
const r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
/** | ||
* @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'; | ||
function initialize(app, siteKey) { | ||
const state = getState(app); | ||
const initialized = new Deferred(); | ||
setState(app, Object.assign(Object.assign({}, state), { reCAPTCHAState: { initialized } })); | ||
const divId = `fire_app_check_${app.name}`; | ||
const invisibleDiv = document.createElement('div'); | ||
invisibleDiv.id = divId; | ||
invisibleDiv.style.display = 'none'; | ||
document.body.appendChild(invisibleDiv); | ||
const grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
loadReCAPTCHAScript(() => { | ||
const grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
// it shouldn't happen. | ||
throw new Error('no recaptcha'); | ||
} | ||
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, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
}); | ||
} | ||
else { | ||
grecaptcha.ready(() => { | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
} | ||
return initialized.promise; | ||
} | ||
async function getToken$2(app) { | ||
ensureActivated(app); | ||
// ensureActivated() guarantees that reCAPTCHAState is set | ||
const reCAPTCHAState = getState(app).reCAPTCHAState; | ||
const recaptcha = await reCAPTCHAState.initialized.promise; | ||
return new Promise((resolve, _reject) => { | ||
// Updated after initialization is complete. | ||
const reCAPTCHAState = getState(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' | ||
}); | ||
const state = getState(app); | ||
setState(app, Object.assign(Object.assign({}, state), { reCAPTCHAState: Object.assign(Object.assign({}, state.reCAPTCHAState), { // state.reCAPTCHAState is set in the initialize() | ||
widgetId }) })); | ||
} | ||
function loadReCAPTCHAScript(onload) { | ||
const script = document.createElement('script'); | ||
script.src = `${RECAPTCHA_URL}`; | ||
script.onload = onload; | ||
document.head.appendChild(script); | ||
} | ||
/** | ||
* @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/v1beta'; | ||
@@ -358,2 +235,47 @@ const EXCHANGE_RECAPTCHA_TOKEN_METHOD = 'exchangeRecaptchaToken'; | ||
*/ | ||
function getRecaptcha() { | ||
return self.grecaptcha; | ||
} | ||
function ensureActivated(app) { | ||
if (!getState(app).activated) { | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: app.name | ||
}); | ||
} | ||
} | ||
/** | ||
* Copied from https://stackoverflow.com/a/2117523 | ||
*/ | ||
function uuidv4() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { | ||
const r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
/** | ||
* Stringify and base64 encode token error data. | ||
* | ||
* @param tokenError Error data, currently hardcoded. | ||
*/ | ||
function formatDummyToken(tokenErrorData) { | ||
return base64.encodeString(JSON.stringify(tokenErrorData), | ||
/* webSafe= */ false); | ||
} | ||
/** | ||
* @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 }, platformLoggerProvider) { | ||
@@ -719,11 +641,2 @@ const headers = { | ||
/** | ||
* Stringify and base64 encode token error data. | ||
* | ||
* @param tokenError Error data, currently hardcoded. | ||
*/ | ||
function formatDummyToken(tokenErrorData) { | ||
return base64.encodeString(JSON.stringify(tokenErrorData), | ||
/* webSafe= */ false); | ||
} | ||
/** | ||
* This function will always resolve. | ||
@@ -733,3 +646,3 @@ * The result will contain an error field if there is any error. | ||
*/ | ||
async function getToken$1(app, platformLoggerProvider, forceRefresh = false) { | ||
async function getToken$2(app, platformLoggerProvider, forceRefresh = false) { | ||
ensureActivated(app); | ||
@@ -746,4 +659,4 @@ const state = getState(app); | ||
if (!token) { | ||
// readTokenFromStorage() always resolves. In case of an error, it resolves with `undefined`. | ||
const cachedToken = await readTokenFromStorage(app); | ||
// cachedTokenPromise contains the token found in IndexedDB or undefined if not found. | ||
const cachedToken = await state.cachedTokenPromise; | ||
if (cachedToken && isValid(cachedToken)) { | ||
@@ -779,23 +692,6 @@ token = cachedToken; | ||
try { | ||
if (state.customProvider) { | ||
const customToken = await state.customProvider.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 = 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(); | ||
token = Object.assign(Object.assign({}, customToken), { issuedAtTimeMillis }); | ||
} | ||
else { | ||
const attestedClaimsToken = await getToken$2(app).catch(_e => { | ||
// reCaptcha.execute() throws null which is not very descriptive. | ||
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */); | ||
}); | ||
token = await exchangeToken(getExchangeRecaptchaTokenRequest(app, attestedClaimsToken), platformLoggerProvider); | ||
} | ||
// state.provider is populated in initializeAppCheck() | ||
// ensureActivated() at the top of this function checks that | ||
// initializeAppCheck() has been called. | ||
token = await state.provider.getToken(); | ||
} | ||
@@ -845,3 +741,4 @@ catch (e) { | ||
} | ||
// invoke the listener async immediately if there is a valid token | ||
// Invoke the listener async immediately if there is a valid token | ||
// in memory. | ||
if (state.token && isValid(state.token)) { | ||
@@ -855,2 +752,16 @@ const validToken = state.token; | ||
} | ||
else if (state.token == null) { | ||
// Only check cache if there was no token. If the token was invalid, | ||
// skip this and rely on exchange endpoint. | ||
void state | ||
.cachedTokenPromise // Storage token promise. Always populated in `activate()`. | ||
.then(cachedToken => { | ||
if (cachedToken && isValid(cachedToken)) { | ||
listener({ token: cachedToken.token }); | ||
} | ||
}) | ||
.catch(() => { | ||
/** Ignore errors in listeners. */ | ||
}); | ||
} | ||
setState(app, newState); | ||
@@ -878,6 +789,6 @@ } | ||
if (!state.token) { | ||
result = await getToken$1(app, platformLoggerProvider); | ||
result = await getToken$2(app, platformLoggerProvider); | ||
} | ||
else { | ||
result = await getToken$1(app, platformLoggerProvider, true); | ||
result = await getToken$2(app, platformLoggerProvider, true); | ||
} | ||
@@ -957,11 +868,198 @@ // getToken() always resolves. In case the result has an error field defined, it means the operation failed, and we should retry. | ||
*/ | ||
const RECAPTCHA_URL = 'https://www.google.com/recaptcha/api.js'; | ||
function initialize(app, siteKey) { | ||
const state = getState(app); | ||
const initialized = new Deferred(); | ||
setState(app, Object.assign(Object.assign({}, state), { reCAPTCHAState: { initialized } })); | ||
const divId = `fire_app_check_${app.name}`; | ||
const invisibleDiv = document.createElement('div'); | ||
invisibleDiv.id = divId; | ||
invisibleDiv.style.display = 'none'; | ||
document.body.appendChild(invisibleDiv); | ||
const grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
loadReCAPTCHAScript(() => { | ||
const grecaptcha = getRecaptcha(); | ||
if (!grecaptcha) { | ||
// it shouldn't happen. | ||
throw new Error('no recaptcha'); | ||
} | ||
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, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
}); | ||
} | ||
else { | ||
grecaptcha.ready(() => { | ||
renderInvisibleWidget(app, siteKey, grecaptcha, divId); | ||
initialized.resolve(grecaptcha); | ||
}); | ||
} | ||
return initialized.promise; | ||
} | ||
async function getToken$1(app) { | ||
ensureActivated(app); | ||
// ensureActivated() guarantees that reCAPTCHAState is set | ||
const reCAPTCHAState = getState(app).reCAPTCHAState; | ||
const recaptcha = await reCAPTCHAState.initialized.promise; | ||
return new Promise((resolve, _reject) => { | ||
// Updated after initialization is complete. | ||
const reCAPTCHAState = getState(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' | ||
}); | ||
const state = getState(app); | ||
setState(app, Object.assign(Object.assign({}, state), { reCAPTCHAState: Object.assign(Object.assign({}, state.reCAPTCHAState), { // state.reCAPTCHAState is set in the initialize() | ||
widgetId }) })); | ||
} | ||
function loadReCAPTCHAScript(onload) { | ||
const script = document.createElement('script'); | ||
script.src = `${RECAPTCHA_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. | ||
*/ | ||
class ReCaptchaV3Provider { | ||
/** | ||
* Create a ReCaptchaV3Provider instance. | ||
* @param siteKey - ReCAPTCHA V3 siteKey. | ||
*/ | ||
constructor(_siteKey) { | ||
this._siteKey = _siteKey; | ||
} | ||
/** | ||
* Returns an App Check token. | ||
* @internal | ||
*/ | ||
async getToken() { | ||
if (!this._app || !this._platformLoggerProvider) { | ||
// This should only occur if user has not called initializeAppCheck(). | ||
// We don't have an appName to provide if so. | ||
// This should already be caught in the top level `getToken()` function. | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: '' | ||
}); | ||
} | ||
let attestedClaimsToken; | ||
try { | ||
attestedClaimsToken = await getToken$1(this._app); | ||
} | ||
catch (e) { | ||
// reCaptcha.execute() throws null which is not very descriptive. | ||
throw ERROR_FACTORY.create("recaptcha-error" /* RECAPTCHA_ERROR */); | ||
} | ||
return exchangeToken(getExchangeRecaptchaTokenRequest(this._app, attestedClaimsToken), this._platformLoggerProvider); | ||
} | ||
initialize(app, platformLoggerProvider) { | ||
this._app = app; | ||
this._platformLoggerProvider = platformLoggerProvider; | ||
initialize(app, this._siteKey).catch(() => { | ||
/* we don't care about the initialization result */ | ||
}); | ||
} | ||
} | ||
/** | ||
* Custom provider class. | ||
*/ | ||
class CustomProvider { | ||
constructor(_customProviderOptions) { | ||
this._customProviderOptions = _customProviderOptions; | ||
} | ||
/** | ||
* @internal | ||
*/ | ||
async getToken() { | ||
if (!this._app) { | ||
// This should only occur if user has not called initializeAppCheck(). | ||
// We don't have an appName to provide if so. | ||
// This should already be caught in the top level `getToken()` function. | ||
throw ERROR_FACTORY.create("use-before-activation" /* USE_BEFORE_ACTIVATION */, { | ||
appName: '' | ||
}); | ||
} | ||
// 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 = 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 Object.assign(Object.assign({}, customToken), { issuedAtTimeMillis }); | ||
} | ||
/** | ||
* @internal | ||
*/ | ||
initialize(app) { | ||
this._app = app; | ||
} | ||
} | ||
/** | ||
* @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. | ||
*/ | ||
/** | ||
* | ||
* @param app | ||
* @param siteKeyOrProvider - optional custom attestation provider | ||
* or reCAPTCHA siteKey | ||
* or reCAPTCHA provider | ||
* @param isTokenAutoRefreshEnabled - if true, enables auto refresh | ||
* of appCheck token. | ||
*/ | ||
function activate(app, siteKeyOrProvider, isTokenAutoRefreshEnabled) { | ||
function activate(app, siteKeyOrProvider, platformLoggerProvider, isTokenAutoRefreshEnabled) { | ||
const state = getState(app); | ||
@@ -974,7 +1072,23 @@ if (state.activated) { | ||
const newState = Object.assign(Object.assign({}, state), { activated: true }); | ||
// Read cached token from storage if it exists and store it in memory. | ||
newState.cachedTokenPromise = readTokenFromStorage(app).then(cachedToken => { | ||
if (cachedToken && isValid(cachedToken)) { | ||
setState(app, Object.assign(Object.assign({}, getState(app)), { token: cachedToken })); | ||
} | ||
return cachedToken; | ||
}); | ||
if (typeof siteKeyOrProvider === 'string') { | ||
newState.siteKey = siteKeyOrProvider; | ||
newState.provider = new ReCaptchaV3Provider(siteKeyOrProvider); | ||
} | ||
else if (siteKeyOrProvider instanceof ReCaptchaV3Provider || | ||
siteKeyOrProvider instanceof CustomProvider) { | ||
newState.provider = siteKeyOrProvider; | ||
} | ||
else { | ||
newState.customProvider = siteKeyOrProvider; | ||
// Process "old" custom provider to avoid breaking previous users. | ||
// This was defined at beta release as simply an object with a | ||
// getToken() method. | ||
newState.provider = new CustomProvider({ | ||
getToken: siteKeyOrProvider.getToken | ||
}); | ||
} | ||
@@ -989,8 +1103,3 @@ // Use value of global `automaticDataCollectionEnabled` (which | ||
setState(app, newState); | ||
// initialize reCAPTCHA if siteKey is provided | ||
if (newState.siteKey) { | ||
initialize(app, newState.siteKey).catch(() => { | ||
/* we don't care about the initialization result in activate() */ | ||
}); | ||
} | ||
newState.provider.initialize(app, platformLoggerProvider); | ||
} | ||
@@ -1015,3 +1124,3 @@ function setTokenAutoRefreshEnabled(app, isTokenAutoRefreshEnabled) { | ||
async function getToken(app, platformLoggerProvider, forceRefresh) { | ||
const result = await getToken$1(app, platformLoggerProvider, forceRefresh); | ||
const result = await getToken$2(app, platformLoggerProvider, forceRefresh); | ||
if (result.error) { | ||
@@ -1068,3 +1177,3 @@ throw result.error; | ||
app, | ||
activate: (siteKeyOrProvider, isTokenAutoRefreshEnabled) => activate(app, siteKeyOrProvider, isTokenAutoRefreshEnabled), | ||
activate: (siteKeyOrProvider, isTokenAutoRefreshEnabled) => activate(app, siteKeyOrProvider, platformLoggerProvider, isTokenAutoRefreshEnabled), | ||
setTokenAutoRefreshEnabled: (isTokenAutoRefreshEnabled) => setTokenAutoRefreshEnabled(app, isTokenAutoRefreshEnabled), | ||
@@ -1092,3 +1201,3 @@ getToken: forceRefresh => getToken(app, platformLoggerProvider, forceRefresh), | ||
return { | ||
getToken: forceRefresh => getToken$1(app, platformLoggerProvider, forceRefresh), | ||
getToken: forceRefresh => getToken$2(app, platformLoggerProvider, forceRefresh), | ||
addTokenListener: listener => addTokenListener(app, platformLoggerProvider, "INTERNAL" /* INTERNAL */, listener), | ||
@@ -1100,3 +1209,3 @@ removeTokenListener: listener => removeTokenListener(app, listener) | ||
const name = "@firebase/app-check"; | ||
const version = "0.2.1-canary.2493c171e"; | ||
const version = "0.2.1-canary.8599d9141"; | ||
@@ -1129,2 +1238,6 @@ /** | ||
}, "PUBLIC" /* PUBLIC */) | ||
.setServiceProps({ | ||
ReCaptchaV3Provider: ReCaptchaV3Provider, | ||
CustomProvider: CustomProvider | ||
}) | ||
/** | ||
@@ -1131,0 +1244,0 @@ * AppCheck can only be initialized by explicitly calling firebase.appCheck() |
@@ -21,2 +21,3 @@ /** | ||
import { PartialObserver, Unsubscribe } from '@firebase/util'; | ||
import { CustomProvider, ReCaptchaV3Provider } from './providers'; | ||
/** | ||
@@ -26,7 +27,7 @@ * | ||
* @param siteKeyOrProvider - optional custom attestation provider | ||
* or reCAPTCHA siteKey | ||
* or reCAPTCHA provider | ||
* @param isTokenAutoRefreshEnabled - if true, enables auto refresh | ||
* of appCheck token. | ||
*/ | ||
export declare function activate(app: FirebaseApp, siteKeyOrProvider: string | AppCheckProvider, isTokenAutoRefreshEnabled?: boolean): void; | ||
export declare function activate(app: FirebaseApp, siteKeyOrProvider: ReCaptchaV3Provider | CustomProvider | AppCheckProvider | string, platformLoggerProvider: Provider<'platform-logger'>, isTokenAutoRefreshEnabled?: boolean): void; | ||
export declare function setTokenAutoRefreshEnabled(app: FirebaseApp, isTokenAutoRefreshEnabled: boolean): void; | ||
@@ -33,0 +34,0 @@ /** |
@@ -1,2 +0,2 @@ | ||
import { FirebaseAppCheck } from '@firebase/app-check-types'; | ||
import { FirebaseAppCheck, ReCaptchaV3Provider, CustomProvider } from '@firebase/app-check-types'; | ||
/** | ||
@@ -8,2 +8,4 @@ * Define extension behavior of `registerAnalytics` | ||
appCheck(app?: FirebaseApp): FirebaseAppCheck; | ||
ReCaptchaV3Provider: typeof ReCaptchaV3Provider; | ||
CustomProvider: typeof CustomProvider; | ||
} | ||
@@ -10,0 +12,0 @@ interface FirebaseApp { |
@@ -19,3 +19,3 @@ /** | ||
import { AppCheckTokenListener, AppCheckTokenResult } from '@firebase/app-check-interop-types'; | ||
import { ListenerType } from './state'; | ||
import { AppCheckTokenInternal, ListenerType } from './state'; | ||
import { Provider } from '@firebase/component'; | ||
@@ -26,8 +26,2 @@ export declare const defaultTokenErrorData: { | ||
/** | ||
* Stringify and base64 encode token error data. | ||
* | ||
* @param tokenError Error data, currently hardcoded. | ||
*/ | ||
export declare function formatDummyToken(tokenErrorData: Record<string, string>): string; | ||
/** | ||
* This function will always resolve. | ||
@@ -40,1 +34,2 @@ * The result will contain an error field if there is any error. | ||
export declare function removeTokenListener(app: FirebaseApp, listener: (token: AppCheckTokenResult) => void): void; | ||
export declare function isValid(token: AppCheckTokenInternal): boolean; |
@@ -18,3 +18,3 @@ /** | ||
import { FirebaseApp } from '@firebase/app-types'; | ||
import { AppCheckProvider, AppCheckToken, AppCheckTokenResult } from '@firebase/app-check-types'; | ||
import { AppCheckToken, AppCheckTokenResult } from '@firebase/app-check-types'; | ||
import { AppCheckTokenListener } from '@firebase/app-check-interop-types'; | ||
@@ -24,2 +24,3 @@ import { Refresher } from './proactive-refresh'; | ||
import { GreCAPTCHA } from './recaptcha'; | ||
import { AppCheckProviderInternal } from './providers'; | ||
export interface AppCheckTokenInternal extends AppCheckToken { | ||
@@ -39,5 +40,6 @@ issuedAtTimeMillis: number; | ||
tokenObservers: AppCheckTokenObserver[]; | ||
customProvider?: AppCheckProvider; | ||
provider?: AppCheckProviderInternal; | ||
siteKey?: string; | ||
token?: AppCheckTokenInternal; | ||
cachedTokenPromise?: Promise<AppCheckTokenInternal | undefined>; | ||
tokenRefresher?: Refresher; | ||
@@ -44,0 +46,0 @@ reCAPTCHAState?: ReCAPTCHAState; |
@@ -25,1 +25,7 @@ /** | ||
export declare function uuidv4(): string; | ||
/** | ||
* Stringify and base64 encode token error data. | ||
* | ||
* @param tokenError Error data, currently hardcoded. | ||
*/ | ||
export declare function formatDummyToken(tokenErrorData: Record<string, string>): string; |
{ | ||
"name": "@firebase/app-check", | ||
"version": "0.2.1-canary.2493c171e", | ||
"version": "0.2.1-canary.8599d9141", | ||
"description": "The App Check component of the Firebase JS SDK", | ||
@@ -27,11 +27,11 @@ "author": "Firebase <firebase-support@google.com> (https://firebase.google.com/)", | ||
"peerDependencies": { | ||
"@firebase/app": "0.6.29-canary.2493c171e", | ||
"@firebase/app-types": "0.6.3-canary.2493c171e" | ||
"@firebase/app": "0.6.29-canary.8599d9141", | ||
"@firebase/app-types": "0.6.3-canary.8599d9141" | ||
}, | ||
"dependencies": { | ||
"@firebase/app-check-types": "0.2.0-canary.2493c171e", | ||
"@firebase/app-check-interop-types": "0.1.0-canary.2493c171e", | ||
"@firebase/util": "1.2.0-canary.2493c171e", | ||
"@firebase/component": "0.5.5-canary.2493c171e", | ||
"@firebase/logger": "0.2.6-canary.2493c171e", | ||
"@firebase/app-check-types": "0.2.0-canary.8599d9141", | ||
"@firebase/app-check-interop-types": "0.1.0-canary.8599d9141", | ||
"@firebase/util": "1.2.0-canary.8599d9141", | ||
"@firebase/component": "0.5.5-canary.8599d9141", | ||
"@firebase/logger": "0.2.6-canary.8599d9141", | ||
"tslib": "^2.1.0" | ||
@@ -41,3 +41,3 @@ }, | ||
"devDependencies": { | ||
"@firebase/app": "0.6.29-canary.2493c171e", | ||
"@firebase/app": "0.6.29-canary.8599d9141", | ||
"rollup": "2.52.2", | ||
@@ -44,0 +44,0 @@ "@rollup/plugin-json": "4.1.0", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
452444
34
4725
+ Added@firebase/app@0.6.29-canary.8599d9141(transitive)
+ Added@firebase/app-check-interop-types@0.1.0-canary.8599d9141(transitive)
+ Added@firebase/app-check-types@0.2.0-canary.8599d9141(transitive)
+ Added@firebase/app-types@0.6.3-canary.8599d9141(transitive)
+ Added@firebase/component@0.5.5-canary.8599d9141(transitive)
+ Added@firebase/logger@0.2.6-canary.8599d9141(transitive)
+ Added@firebase/util@1.2.0-canary.8599d9141(transitive)
- Removed@firebase/app@0.6.29-canary.2493c171e(transitive)
- Removed@firebase/app-check-interop-types@0.1.0-canary.2493c171e(transitive)
- Removed@firebase/app-check-types@0.2.0-canary.2493c171e(transitive)
- Removed@firebase/app-types@0.6.3-canary.2493c171e(transitive)
- Removed@firebase/component@0.5.5-canary.2493c171e(transitive)
- Removed@firebase/logger@0.2.6-canary.2493c171e(transitive)
- Removed@firebase/util@1.2.0-canary.2493c171e(transitive)
Updated@firebase/app-check-interop-types@0.1.0-canary.8599d9141