bitwarden-estensione-browser/apps/browser/src/vault/fido2/content/content-script.ts

141 lines
4.5 KiB
TypeScript

import {
AssertCredentialParams,
CreateCredentialParams,
} from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction";
import { sendExtensionMessage } from "../../../autofill/utils";
import { Fido2PortName } from "../enums/fido2-port-name.enum";
import {
InsecureAssertCredentialParams,
InsecureCreateCredentialParams,
Message,
MessageType,
} from "./messaging/message";
import { MessageWithMetadata, Messenger } from "./messaging/messenger";
(function (globalContext) {
const shouldExecuteContentScript =
globalContext.document.contentType === "text/html" &&
globalContext.document.location.protocol === "https:";
if (!shouldExecuteContentScript) {
return;
}
// Initialization logic, set up the messenger and connect a port to the background script.
const messenger = Messenger.forDOMCommunication(globalContext.window);
messenger.handler = handleFido2Message;
const port = chrome.runtime.connect({ name: Fido2PortName.InjectedScript });
port.onDisconnect.addListener(handlePortOnDisconnect);
/**
* Handles FIDO2 credential requests and returns the result.
*
* @param message - The message to handle.
* @param abortController - The abort controller used to handle exit conditions from the FIDO2 request.
*/
async function handleFido2Message(
message: MessageWithMetadata,
abortController: AbortController,
) {
const requestId = Date.now().toString();
const abortHandler = () =>
sendExtensionMessage("fido2AbortRequest", { abortedRequestId: requestId });
abortController.signal.addEventListener("abort", abortHandler);
try {
if (message.type === MessageType.CredentialCreationRequest) {
return handleCredentialCreationRequestMessage(
requestId,
message.data as InsecureCreateCredentialParams,
);
}
if (message.type === MessageType.CredentialGetRequest) {
return handleCredentialGetRequestMessage(
requestId,
message.data as InsecureAssertCredentialParams,
);
}
} finally {
abortController.signal.removeEventListener("abort", abortHandler);
}
}
/**
* Handles the credential creation request message and returns the result.
*
* @param requestId - The request ID of the message.
* @param data - Data associated with the credential request.
*/
async function handleCredentialCreationRequestMessage(
requestId: string,
data: InsecureCreateCredentialParams,
): Promise<Message | undefined> {
return respondToCredentialRequest(
"fido2RegisterCredentialRequest",
MessageType.CredentialCreationResponse,
requestId,
data,
);
}
/**
* Handles the credential get request message and returns the result.
*
* @param requestId - The request ID of the message.
* @param data - Data associated with the credential request.
*/
async function handleCredentialGetRequestMessage(
requestId: string,
data: InsecureAssertCredentialParams,
): Promise<Message | undefined> {
return respondToCredentialRequest(
"fido2GetCredentialRequest",
MessageType.CredentialGetResponse,
requestId,
data,
);
}
/**
* Sends a message to the extension to handle the
* credential request and returns the result.
*
* @param command - The command to send to the extension.
* @param type - The type of message, either CredentialCreationResponse or CredentialGetResponse.
* @param requestId - The request ID of the message.
* @param messageData - Data associated with the credential request.
*/
async function respondToCredentialRequest(
command: string,
type: MessageType.CredentialCreationResponse | MessageType.CredentialGetResponse,
requestId: string,
messageData: InsecureCreateCredentialParams | InsecureAssertCredentialParams,
): Promise<Message | undefined> {
const data: CreateCredentialParams | AssertCredentialParams = {
...messageData,
origin: globalContext.location.origin,
sameOriginWithAncestors: globalContext.self === globalContext.top,
};
const result = await sendExtensionMessage(command, { data, requestId });
if (result && result.error !== undefined) {
return Promise.reject(result.error);
}
return Promise.resolve({ type, result });
}
/**
* Handles the disconnect event of the port. Calls
* to the messenger to destroy and tear down the
* implemented page-script.js logic.
*/
function handlePortOnDisconnect() {
void messenger.destroy();
}
})(globalThis);