/******/ (function() { // webpackBootstrap /******/ "use strict"; ;// ../../libs/common/src/platform/services/fido2/fido2-utils.ts // @ts-strict-ignore class Fido2Utils { static createResultToJson(result) { return { id: result.credentialId, rawId: result.credentialId, response: { clientDataJSON: result.clientDataJSON, authenticatorData: result.authData, transports: result.transports, publicKey: result.publicKey, publicKeyAlgorithm: result.publicKeyAlgorithm, attestationObject: result.attestationObject, }, authenticatorAttachment: "platform", clientExtensionResults: result.extensions, type: "public-key", }; } static getResultToJson(result) { return { id: result.credentialId, rawId: result.credentialId, response: { clientDataJSON: result.clientDataJSON, authenticatorData: result.authenticatorData, signature: result.signature, userHandle: result.userHandle, }, authenticatorAttachment: "platform", clientExtensionResults: {}, type: "public-key", }; } static bufferToString(bufferSource) { return Fido2Utils.fromBufferToB64(Fido2Utils.bufferSourceToUint8Array(bufferSource)) .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=/g, ""); } static stringToBuffer(str) { return Fido2Utils.fromB64ToArray(Fido2Utils.fromUrlB64ToB64(str)).buffer; } static bufferSourceToUint8Array(bufferSource) { if (Fido2Utils.isArrayBuffer(bufferSource)) { return new Uint8Array(bufferSource); } else { return new Uint8Array(bufferSource.buffer, bufferSource.byteOffset, bufferSource.byteLength); } } /** Utility function to identify type of bufferSource. Necessary because of differences between runtimes */ static isArrayBuffer(bufferSource) { return bufferSource instanceof ArrayBuffer || bufferSource.buffer === undefined; } static fromB64toUrlB64(b64Str) { return b64Str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); } static fromBufferToB64(buffer) { if (buffer == null) { return null; } let binary = ""; const bytes = new Uint8Array(buffer); for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } return globalThis.btoa(binary); } static fromB64ToArray(str) { if (str == null) { return null; } const binaryString = globalThis.atob(str); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes; } static fromUrlB64ToB64(urlB64Str) { let output = urlB64Str.replace(/-/g, "+").replace(/_/g, "/"); switch (output.length % 4) { case 0: break; case 2: output += "=="; break; case 3: output += "="; break; default: throw new Error("Illegal base64url string!"); } return output; } /** * This methods returns true if a cipher either has no passkeys, or has a passkey matching with userHandle * @param userHandle */ static cipherHasNoOtherPasskeys(cipher, userHandle) { if (cipher.login.fido2Credentials == null || cipher.login.fido2Credentials.length === 0) { return true; } return cipher.login.fido2Credentials.some((passkey) => passkey.userHandle === userHandle); } } ;// ./src/autofill/fido2/utils/webauthn-utils.ts class WebauthnUtils { static mapCredentialCreationOptions(options, fallbackSupported) { var _a, _b, _c, _d, _e; const keyOptions = options.publicKey; if (keyOptions == undefined) { throw new Error("Public-key options not found"); } return { attestation: keyOptions.attestation, authenticatorSelection: { requireResidentKey: (_a = keyOptions.authenticatorSelection) === null || _a === void 0 ? void 0 : _a.requireResidentKey, residentKey: (_b = keyOptions.authenticatorSelection) === null || _b === void 0 ? void 0 : _b.residentKey, userVerification: (_c = keyOptions.authenticatorSelection) === null || _c === void 0 ? void 0 : _c.userVerification, }, challenge: Fido2Utils.bufferToString(keyOptions.challenge), excludeCredentials: (_d = keyOptions.excludeCredentials) === null || _d === void 0 ? void 0 : _d.map((credential) => ({ id: Fido2Utils.bufferToString(credential.id), transports: credential.transports, type: credential.type, })), extensions: { credProps: (_e = keyOptions.extensions) === null || _e === void 0 ? void 0 : _e.credProps, }, pubKeyCredParams: keyOptions.pubKeyCredParams .map((params) => ({ // Fix for spec-deviation: Sites using KeycloakJS send `kp.alg` as a string alg: Number(params.alg), type: params.type, })) .filter((params) => !isNaN(params.alg)), rp: { id: keyOptions.rp.id, name: keyOptions.rp.name, }, user: { id: Fido2Utils.bufferToString(keyOptions.user.id), displayName: keyOptions.user.displayName, name: keyOptions.user.name, }, timeout: keyOptions.timeout, fallbackSupported, }; } static mapCredentialRegistrationResult(result) { const credential = { id: result.credentialId, rawId: Fido2Utils.stringToBuffer(result.credentialId), type: "public-key", authenticatorAttachment: "platform", response: { clientDataJSON: Fido2Utils.stringToBuffer(result.clientDataJSON), attestationObject: Fido2Utils.stringToBuffer(result.attestationObject), getAuthenticatorData() { return Fido2Utils.stringToBuffer(result.authData); }, getPublicKey() { return Fido2Utils.stringToBuffer(result.publicKey); }, getPublicKeyAlgorithm() { return result.publicKeyAlgorithm; }, getTransports() { return result.transports; }, }, getClientExtensionResults: () => ({ credProps: result.extensions.credProps, }), toJSON: () => Fido2Utils.createResultToJson(result), }; // Modify prototype chains to fix `instanceof` calls. // This makes these objects indistinguishable from the native classes. // Unfortunately PublicKeyCredential does not have a javascript constructor so `extends` does not work here. Object.setPrototypeOf(credential.response, AuthenticatorAttestationResponse.prototype); Object.setPrototypeOf(credential, PublicKeyCredential.prototype); return credential; } static mapCredentialRequestOptions(options, fallbackSupported) { var _a, _b; const keyOptions = options.publicKey; if (keyOptions == undefined) { throw new Error("Public-key options not found"); } return { allowedCredentialIds: (_b = (_a = keyOptions.allowCredentials) === null || _a === void 0 ? void 0 : _a.map((c) => Fido2Utils.bufferToString(c.id))) !== null && _b !== void 0 ? _b : [], challenge: Fido2Utils.bufferToString(keyOptions.challenge), rpId: keyOptions.rpId, userVerification: keyOptions.userVerification, timeout: keyOptions.timeout, mediation: options.mediation, fallbackSupported, }; } static mapCredentialAssertResult(result) { const credential = { id: result.credentialId, rawId: Fido2Utils.stringToBuffer(result.credentialId), type: "public-key", response: { authenticatorData: Fido2Utils.stringToBuffer(result.authenticatorData), clientDataJSON: Fido2Utils.stringToBuffer(result.clientDataJSON), signature: Fido2Utils.stringToBuffer(result.signature), userHandle: Fido2Utils.stringToBuffer(result.userHandle), }, getClientExtensionResults: () => ({}), authenticatorAttachment: "platform", toJSON: () => Fido2Utils.getResultToJson(result), }; // Modify prototype chains to fix `instanceof` calls. // This makes these objects indistinguishable from the native classes. // Unfortunately PublicKeyCredential does not have a javascript constructor so `extends` does not work here. Object.setPrototypeOf(credential.response, AuthenticatorAssertionResponse.prototype); Object.setPrototypeOf(credential, PublicKeyCredential.prototype); return credential; } } ;// ./src/autofill/fido2/content/messaging/message.ts const MessageTypes = { CredentialCreationRequest: 0, CredentialCreationResponse: 1, CredentialGetRequest: 2, CredentialGetResponse: 3, AbortRequest: 4, DisconnectRequest: 5, ReconnectRequest: 6, AbortResponse: 7, ErrorResponse: 8, }; ;// ./src/autofill/fido2/content/messaging/messenger.ts var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore const SENDER = "bitwarden-webauthn"; /** * A class that handles communication between the page and content script. It converts * the browser's broadcasting API into a request/response API with support for seamlessly * handling aborts and exceptions across separate execution contexts. */ class Messenger { /** * Creates a messenger that uses the browser's `window.postMessage` API to initiate * requests in the content script. Every request will then create it's own * `MessageChannel` through which all subsequent communication will be sent through. * * @param window the window object to use for communication * @returns a `Messenger` instance */ static forDOMCommunication(window) { const windowOrigin = window.location.origin; return new Messenger({ postMessage: (message, port) => window.postMessage(message, windowOrigin, [port]), addEventListener: (listener) => window.addEventListener("message", listener), removeEventListener: (listener) => window.removeEventListener("message", listener), }); } constructor(broadcastChannel) { this.broadcastChannel = broadcastChannel; this.messageEventListener = null; this.onDestroy = new EventTarget(); this.messengerId = this.generateUniqueId(); this.messageEventListener = this.createMessageEventListener(); this.broadcastChannel.addEventListener(this.messageEventListener); } /** * Sends a request to the content script and returns the response. * AbortController signals will be forwarded to the content script. * * @param request data to send to the content script * @param abortSignal the abort controller that might be used to abort the request * @returns the response from the content script */ request(request, abortSignal) { return __awaiter(this, void 0, void 0, function* () { const requestChannel = new MessageChannel(); const { port1: localPort, port2: remotePort } = requestChannel; try { const promise = new Promise((resolve) => { localPort.onmessage = (event) => resolve(event.data); }); const abortListener = () => localPort.postMessage({ metadata: { SENDER }, type: MessageTypes.AbortRequest, }); abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.addEventListener("abort", abortListener); this.broadcastChannel.postMessage(Object.assign(Object.assign({}, request), { SENDER, senderId: this.messengerId }), remotePort); const response = yield promise; abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.removeEventListener("abort", abortListener); if (response.type === MessageTypes.ErrorResponse) { const error = new Error(); Object.assign(error, JSON.parse(response.error)); throw error; } return response; } finally { localPort.close(); } }); } createMessageEventListener() { return (event) => __awaiter(this, void 0, void 0, function* () { var _a; const windowOrigin = window.location.origin; if (event.origin !== windowOrigin || !this.handler) { return; } const message = event.data; const port = (_a = event.ports) === null || _a === void 0 ? void 0 : _a[0]; if ((message === null || message === void 0 ? void 0 : message.SENDER) !== SENDER || message.senderId == this.messengerId || port == null) { return; } const abortController = new AbortController(); port.onmessage = (event) => { if (event.data.type === MessageTypes.AbortRequest) { abortController.abort(); } }; const onDestroyListener = () => abortController.abort(); this.onDestroy.addEventListener("destroy", onDestroyListener); try { const handlerResponse = yield this.handler(message, abortController); port.postMessage(Object.assign(Object.assign({}, handlerResponse), { SENDER })); } catch (error) { port.postMessage({ SENDER, type: MessageTypes.ErrorResponse, error: JSON.stringify(error, Object.getOwnPropertyNames(error)), }); } finally { this.onDestroy.removeEventListener("destroy", onDestroyListener); port.close(); } }); } /** * Cleans up the messenger by removing the message event listener */ destroy() { return __awaiter(this, void 0, void 0, function* () { this.onDestroy.dispatchEvent(new Event("destroy")); if (this.messageEventListener) { yield this.sendDisconnectCommand(); this.broadcastChannel.removeEventListener(this.messageEventListener); this.messageEventListener = null; } }); } sendDisconnectCommand() { return __awaiter(this, void 0, void 0, function* () { yield this.request({ type: MessageTypes.DisconnectRequest }); }); } generateUniqueId() { return Date.now().toString(36) + Math.random().toString(36).substring(2); } } ;// ./src/autofill/fido2/content/fido2-page-script.ts var fido2_page_script_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore (function (globalContext) { if (globalContext.document.currentScript) { globalContext.document.currentScript.parentNode.removeChild(globalContext.document.currentScript); } const shouldExecuteContentScript = globalContext.document.contentType === "text/html" && (globalContext.document.location.protocol === "https:" || (globalContext.document.location.protocol === "http:" && globalContext.document.location.hostname === "localhost")); if (!shouldExecuteContentScript) { return; } const BrowserPublicKeyCredential = globalContext.PublicKeyCredential; const BrowserNavigatorCredentials = navigator.credentials; const BrowserAuthenticatorAttestationResponse = globalContext.AuthenticatorAttestationResponse; const browserNativeWebauthnSupport = globalContext.PublicKeyCredential != undefined; let browserNativeWebauthnPlatformAuthenticatorSupport = false; if (!browserNativeWebauthnSupport) { // Polyfill webauthn support try { // credentials are read-only if supported, use type-casting to force assignment navigator.credentials = { create() { return fido2_page_script_awaiter(this, void 0, void 0, function* () { throw new Error("Webauthn not supported in this browser."); }); }, get() { return fido2_page_script_awaiter(this, void 0, void 0, function* () { throw new Error("Webauthn not supported in this browser."); }); }, }; globalContext.PublicKeyCredential = class PolyfillPublicKeyCredential { static isUserVerifyingPlatformAuthenticatorAvailable() { return Promise.resolve(true); } }; globalContext.AuthenticatorAttestationResponse = class PolyfillAuthenticatorAttestationResponse { }; } catch (_a) { /* empty */ } } else { void BrowserPublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable().then((available) => { browserNativeWebauthnPlatformAuthenticatorSupport = available; if (!available) { // Polyfill platform authenticator support globalContext.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable = () => Promise.resolve(true); } }); } const browserCredentials = { create: navigator.credentials.create.bind(navigator.credentials), get: navigator.credentials.get.bind(navigator.credentials), }; const messenger = Messenger.forDOMCommunication(window); let waitForFocusTimeout; let focusListenerHandler; navigator.credentials.create = createWebAuthnCredential; navigator.credentials.get = getWebAuthnCredential; /** * Creates a new webauthn credential. * * @param options Options for creating new credentials. * @returns Promise that resolves to the new credential object. */ function createWebAuthnCredential(options) { return fido2_page_script_awaiter(this, void 0, void 0, function* () { var _a, _b; if (!isWebauthnCall(options)) { return yield browserCredentials.create(options); } const authenticatorAttachmentIsPlatform = ((_b = (_a = options === null || options === void 0 ? void 0 : options.publicKey) === null || _a === void 0 ? void 0 : _a.authenticatorSelection) === null || _b === void 0 ? void 0 : _b.authenticatorAttachment) === "platform"; const fallbackSupported = (authenticatorAttachmentIsPlatform && browserNativeWebauthnPlatformAuthenticatorSupport) || (!authenticatorAttachmentIsPlatform && browserNativeWebauthnSupport); try { const response = yield messenger.request({ type: MessageTypes.CredentialCreationRequest, data: WebauthnUtils.mapCredentialCreationOptions(options, fallbackSupported), }, options === null || options === void 0 ? void 0 : options.signal); if (response.type !== MessageTypes.CredentialCreationResponse) { throw new Error("Something went wrong."); } return WebauthnUtils.mapCredentialRegistrationResult(response.result); } catch (error) { if (error && error.fallbackRequested && fallbackSupported) { yield waitForFocus(); return yield browserCredentials.create(options); } throw error; } }); } /** * Retrieves a webauthn credential. * * @param options Options for creating new credentials. * @returns Promise that resolves to the new credential object. */ function getWebAuthnCredential(options) { return fido2_page_script_awaiter(this, void 0, void 0, function* () { if (!isWebauthnCall(options)) { return yield browserCredentials.get(options); } const abortSignal = (options === null || options === void 0 ? void 0 : options.signal) || new AbortController().signal; const fallbackSupported = browserNativeWebauthnSupport; if ((options === null || options === void 0 ? void 0 : options.mediation) && options.mediation === "conditional") { const internalAbortControllers = [new AbortController(), new AbortController()]; const bitwardenResponse = (internalAbortController) => fido2_page_script_awaiter(this, void 0, void 0, function* () { try { const abortListener = () => messenger.request({ type: MessageTypes.AbortRequest, abortedRequestId: abortSignal.toString(), }); internalAbortController.signal.addEventListener("abort", abortListener); const response = yield messenger.request({ type: MessageTypes.CredentialGetRequest, data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported), }, internalAbortController.signal); internalAbortController.signal.removeEventListener("abort", abortListener); if (response.type !== MessageTypes.CredentialGetResponse) { throw new Error("Something went wrong."); } return WebauthnUtils.mapCredentialAssertResult(response.result); } catch (_a) { // Ignoring error } }); const browserResponse = (internalAbortController) => browserCredentials.get(Object.assign(Object.assign({}, options), { signal: internalAbortController.signal })); const abortListener = () => { internalAbortControllers.forEach((controller) => controller.abort()); }; abortSignal.addEventListener("abort", abortListener); const response = yield Promise.race([ bitwardenResponse(internalAbortControllers[0]), browserResponse(internalAbortControllers[1]), ]); abortSignal.removeEventListener("abort", abortListener); internalAbortControllers.forEach((controller) => controller.abort()); return response; } try { const response = yield messenger.request({ type: MessageTypes.CredentialGetRequest, data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported), }, options === null || options === void 0 ? void 0 : options.signal); if (response.type !== MessageTypes.CredentialGetResponse) { throw new Error("Something went wrong."); } return WebauthnUtils.mapCredentialAssertResult(response.result); } catch (error) { if (error && error.fallbackRequested && fallbackSupported) { yield waitForFocus(); return yield browserCredentials.get(options); } throw error; } }); } function isWebauthnCall(options) { return options && "publicKey" in options; } /** * Wait for window to be focused. * Safari doesn't allow scripts to trigger webauthn when window is not focused. * * @param fallbackWait How long to wait when the script is not able to add event listeners to `window.top`. Defaults to 500ms. * @param timeout Maximum time to wait for focus in milliseconds. Defaults to 5 minutes. * @returns Promise that resolves when window is focused, or rejects if timeout is reached. */ function waitForFocus() { return fido2_page_script_awaiter(this, arguments, void 0, function* (fallbackWait = 500, timeout = 5 * 60 * 1000) { try { if (globalContext.top.document.hasFocus()) { return; } } catch (_a) { // Cannot access window.top due to cross-origin frame, fallback to waiting return yield new Promise((resolve) => globalContext.setTimeout(resolve, fallbackWait)); } const focusPromise = new Promise((resolve) => { focusListenerHandler = () => resolve(); globalContext.top.addEventListener("focus", focusListenerHandler); }); const timeoutPromise = new Promise((_, reject) => { waitForFocusTimeout = globalContext.setTimeout(() => reject(new DOMException("The operation either timed out or was not allowed.", "AbortError")), timeout); }); try { yield Promise.race([focusPromise, timeoutPromise]); } finally { clearWaitForFocus(); } }); } function clearWaitForFocus() { globalContext.top.removeEventListener("focus", focusListenerHandler); if (waitForFocusTimeout) { globalContext.clearTimeout(waitForFocusTimeout); } } function destroy() { try { if (browserNativeWebauthnSupport) { navigator.credentials.create = browserCredentials.create; navigator.credentials.get = browserCredentials.get; } else { navigator.credentials = BrowserNavigatorCredentials; globalContext.PublicKeyCredential = BrowserPublicKeyCredential; globalContext.AuthenticatorAttestationResponse = BrowserAuthenticatorAttestationResponse; } clearWaitForFocus(); void messenger.destroy(); } catch (_a) { /** empty */ } } /** * Sets up a listener to handle cleanup or reconnection when the extension's * context changes due to being reloaded or unloaded. */ messenger.handler = (message) => { const type = message.type; // Handle cleanup for disconnect request if (type === MessageTypes.DisconnectRequest) { destroy(); } }; })(globalThis); /******/ })() ;