mirror of
https://github.com/bitwarden/browser
synced 2026-02-03 10:13:31 +00:00
Experimental refactor of native-autofill.main.ts
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { autofill, passkey_authenticator } from "@bitwarden/desktop-napi";
|
||||
import { autofill } from "@bitwarden/desktop-napi";
|
||||
|
||||
import { WindowMain } from "../../../main/window.main";
|
||||
|
||||
import { CommandDefinition } from "./command";
|
||||
import { NativeAutofillFido2Credential, NativeAutofillSyncParams } from "./sync.command";
|
||||
import { NativeAutofillWindowsMain } from "./native-autofill.windows.main";
|
||||
|
||||
export type RunCommandParams<C extends CommandDefinition> = {
|
||||
namespace: C["namespace"];
|
||||
@@ -18,254 +18,18 @@ export type RunCommandResult<C extends CommandDefinition> = C["output"];
|
||||
|
||||
export class NativeAutofillMain {
|
||||
private ipcServer: autofill.IpcServer | null;
|
||||
private pendingPasskeyRequests = new Map<string, (response: any) => void>();
|
||||
private windowsMain: NativeAutofillWindowsMain;
|
||||
|
||||
constructor(
|
||||
private logService: LogService,
|
||||
private windowMain: WindowMain,
|
||||
) {}
|
||||
|
||||
initWindows() {
|
||||
passkey_authenticator.register();
|
||||
void passkey_authenticator.onRequest(async (error, event) => {
|
||||
this.logService.info("Passkey request received:", { error, event });
|
||||
|
||||
try {
|
||||
const request = JSON.parse(event.requestJson);
|
||||
this.logService.info("Parsed passkey request:", { type: event.requestType, request });
|
||||
|
||||
// Handle different request types based on the requestType field
|
||||
switch (event.requestType) {
|
||||
case "assertion":
|
||||
return await this.handleAssertionRequest(request);
|
||||
case "registration":
|
||||
return await this.handleRegistrationRequest(request);
|
||||
case "sync":
|
||||
return await this.handleSyncRequest(request);
|
||||
default:
|
||||
this.logService.error("Unknown passkey request type:", event.requestType);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: `Unknown request type: ${event.requestType}`,
|
||||
});
|
||||
}
|
||||
} catch (parseError) {
|
||||
this.logService.error("Failed to parse passkey request:", parseError);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: "Failed to parse request JSON",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async handleAssertionRequest(request: autofill.PasskeyAssertionRequest): Promise<string> {
|
||||
this.logService.info("Handling assertion request for rpId:", request.rpId);
|
||||
|
||||
try {
|
||||
// Generate unique identifiers for tracking this request
|
||||
const clientId = Date.now();
|
||||
const sequenceNumber = Math.floor(Math.random() * 1000000);
|
||||
|
||||
// Send request and wait for response
|
||||
const response = await this.sendAndOptionallyWait<autofill.PasskeyAssertionResponse>(
|
||||
"autofill.passkeyAssertion",
|
||||
{
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
request: request,
|
||||
},
|
||||
{ waitForResponse: true, timeout: 60000 },
|
||||
);
|
||||
|
||||
if (response) {
|
||||
// Convert the response to the format expected by the NAPI bridge
|
||||
return JSON.stringify({
|
||||
type: "assertion_response",
|
||||
...response,
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: "No response received from renderer",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logService.error("Error in assertion request:", error);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: `Assertion request failed: ${error.message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleRegistrationRequest(
|
||||
request: autofill.PasskeyRegistrationRequest,
|
||||
): Promise<string> {
|
||||
this.logService.info("Handling registration request for rpId:", request.rpId);
|
||||
|
||||
try {
|
||||
// Generate unique identifiers for tracking this request
|
||||
const clientId = Date.now();
|
||||
const sequenceNumber = Math.floor(Math.random() * 1000000);
|
||||
|
||||
// Send request and wait for response
|
||||
const response = await this.sendAndOptionallyWait<autofill.PasskeyRegistrationResponse>(
|
||||
"autofill.passkeyRegistration",
|
||||
{
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
request: request,
|
||||
},
|
||||
{ waitForResponse: true, timeout: 60000 },
|
||||
);
|
||||
|
||||
this.logService.info("Received response for registration request:", response);
|
||||
|
||||
if (response) {
|
||||
// Convert the response to the format expected by the NAPI bridge
|
||||
return JSON.stringify({
|
||||
type: "registration_response",
|
||||
...response,
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: "No response received from renderer",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logService.error("Error in registration request:", error);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: `Registration request failed: ${error.message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSyncRequest(
|
||||
request: passkey_authenticator.PasskeySyncRequest,
|
||||
): Promise<string> {
|
||||
this.logService.info("Handling sync request for rpId:", request.rpId);
|
||||
|
||||
try {
|
||||
// Generate unique identifiers for tracking this request
|
||||
const clientId = Date.now();
|
||||
const sequenceNumber = Math.floor(Math.random() * 1000000);
|
||||
|
||||
// Send sync request and wait for response
|
||||
const response = await this.sendAndOptionallyWait<passkey_authenticator.PasskeySyncResponse>(
|
||||
"autofill.passkeySync",
|
||||
{
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
request: { rpId: request.rpId },
|
||||
},
|
||||
{ waitForResponse: true, timeout: 60000 },
|
||||
);
|
||||
|
||||
this.logService.info("Received response for sync request:", response);
|
||||
|
||||
if (response && response.credentials) {
|
||||
// Convert the response to the format expected by the NAPI bridge
|
||||
return JSON.stringify({
|
||||
type: "sync_response",
|
||||
credentials: response.credentials,
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: "No credentials received from renderer",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logService.error("Error in sync request:", error);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: `Sync request failed: ${error.message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for webContents.send that optionally waits for a response
|
||||
* @param channel The IPC channel to send to
|
||||
* @param data The data to send
|
||||
* @param options Optional configuration
|
||||
* @returns Promise that resolves with the response if waitForResponse is true
|
||||
*/
|
||||
private async sendAndOptionallyWait<T = any>(
|
||||
channel: string,
|
||||
data: any,
|
||||
options?: { waitForResponse?: boolean; timeout?: number },
|
||||
): Promise<T | void> {
|
||||
if (!options?.waitForResponse) {
|
||||
// Just send without waiting for response (existing behavior)
|
||||
this.logService.info(`Sending fire-and-forget message to ${channel}`);
|
||||
this.windowMain.win.webContents.send(channel, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use clientId and sequenceNumber as the tracking key
|
||||
const trackingKey = `${data.clientId}_${data.sequenceNumber}`;
|
||||
const timeout = options.timeout || 30000; // 30 second default timeout
|
||||
|
||||
// Send the original data without adding requestId
|
||||
const dataWithId = { ...data };
|
||||
|
||||
this.logService.info(`Sending awaitable request ${trackingKey} to ${channel}`, { dataWithId });
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
// Set up timeout
|
||||
const timeoutId = setTimeout(() => {
|
||||
this.logService.warning(`Request ${trackingKey} timed out after ${timeout}ms`);
|
||||
this.pendingPasskeyRequests.delete(trackingKey);
|
||||
reject(new Error(`Request timeout after ${timeout}ms`));
|
||||
}, timeout);
|
||||
|
||||
// Store the resolver
|
||||
this.pendingPasskeyRequests.set(trackingKey, (response: T) => {
|
||||
this.logService.info(`Request ${trackingKey} resolved with response:`, response);
|
||||
clearTimeout(timeoutId);
|
||||
this.pendingPasskeyRequests.delete(trackingKey);
|
||||
resolve(response);
|
||||
});
|
||||
|
||||
this.logService.info(
|
||||
`Stored resolver for request ${trackingKey}, total pending: ${this.pendingPasskeyRequests.size}`,
|
||||
);
|
||||
|
||||
// Send the request
|
||||
this.windowMain.win.webContents.send(channel, dataWithId);
|
||||
});
|
||||
) {
|
||||
this.windowsMain = new NativeAutofillWindowsMain(logService, windowMain);
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.initWindows();
|
||||
|
||||
ipcMain.handle(
|
||||
"autofill.syncPasskeys",
|
||||
async (event, data: NativeAutofillSyncParams): Promise<string> => {
|
||||
this.logService.info("autofill.syncPasskeys", data);
|
||||
const { credentials } = data;
|
||||
const mapped = credentials.map((cred: NativeAutofillFido2Credential) => {
|
||||
const x: passkey_authenticator.SyncedCredential = {
|
||||
credentialId: cred.credentialId,
|
||||
rpId: cred.rpId,
|
||||
userName: cred.userName,
|
||||
userHandle: cred.userHandle,
|
||||
};
|
||||
this.logService.info("Mapped credential:", x);
|
||||
return x;
|
||||
});
|
||||
|
||||
this.logService.info("Syncing passkeys to Windows:", mapped);
|
||||
|
||||
passkey_authenticator.syncCredentialsToWindows(mapped);
|
||||
|
||||
return "worked";
|
||||
},
|
||||
);
|
||||
this.windowsMain.initWindows();
|
||||
this.windowsMain.setupWindowsRendererIPCHandlers();
|
||||
|
||||
ipcMain.handle(
|
||||
"autofill.runCommand",
|
||||
@@ -322,102 +86,29 @@ export class NativeAutofillMain {
|
||||
|
||||
ipcMain.on("autofill.completePasskeyRegistration", (event, data) => {
|
||||
this.logService.warning("autofill.completePasskeyRegistration", data);
|
||||
const { clientId, sequenceNumber, response, requestId } = data;
|
||||
|
||||
// Handle both IpcServer and awaitable requests
|
||||
const { clientId, sequenceNumber, response } = data;
|
||||
if (this.ipcServer && clientId !== -1) {
|
||||
this.ipcServer.completeRegistration(clientId, sequenceNumber, response);
|
||||
}
|
||||
|
||||
// Handle awaitable passkey requests using clientId and sequenceNumber
|
||||
if (clientId !== undefined && sequenceNumber !== undefined) {
|
||||
const trackingKey = `${clientId}_${sequenceNumber}`;
|
||||
this.handlePasskeyResponse(trackingKey, response);
|
||||
}
|
||||
// Fallback to requestId for backward compatibility
|
||||
else if (requestId) {
|
||||
this.handlePasskeyResponse(requestId, response);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on("autofill.completePasskeyAssertion", (event, data) => {
|
||||
this.logService.warning("autofill.completePasskeyAssertion", data);
|
||||
const { clientId, sequenceNumber, response, requestId } = data;
|
||||
|
||||
// Handle both IpcServer and awaitable requests
|
||||
const { clientId, sequenceNumber, response } = data;
|
||||
if (this.ipcServer && clientId !== -1) {
|
||||
this.ipcServer.completeAssertion(clientId, sequenceNumber, response);
|
||||
}
|
||||
|
||||
// Handle awaitable passkey requests using clientId and sequenceNumber
|
||||
if (clientId !== undefined && sequenceNumber !== undefined) {
|
||||
const trackingKey = `${clientId}_${sequenceNumber}`;
|
||||
this.handlePasskeyResponse(trackingKey, response);
|
||||
}
|
||||
// Fallback to requestId for backward compatibility
|
||||
else if (requestId) {
|
||||
this.handlePasskeyResponse(requestId, response);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on("autofill.completePasskeySync", (event, data) => {
|
||||
this.logService.warning("autofill.completePasskeySync", data);
|
||||
const { clientId, sequenceNumber, response, requestId } = data;
|
||||
|
||||
// Handle awaitable passkey requests using clientId and sequenceNumber
|
||||
if (clientId !== undefined && sequenceNumber !== undefined) {
|
||||
const trackingKey = `${clientId}_${sequenceNumber}`;
|
||||
this.handlePasskeyResponse(trackingKey, response);
|
||||
}
|
||||
// Fallback to requestId for backward compatibility
|
||||
else if (requestId) {
|
||||
this.handlePasskeyResponse(requestId, response);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on("autofill.completeError", (event, data) => {
|
||||
this.logService.warning("autofill.completeError", data);
|
||||
const { clientId, sequenceNumber, error, requestId } = data;
|
||||
|
||||
// Handle both IpcServer and awaitable requests
|
||||
const { clientId, sequenceNumber, error } = data;
|
||||
if (this.ipcServer && clientId !== -1) {
|
||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
||||
}
|
||||
|
||||
// Handle awaitable passkey requests using clientId and sequenceNumber
|
||||
if (clientId !== undefined && sequenceNumber !== undefined) {
|
||||
const trackingKey = `${clientId}_${sequenceNumber}`;
|
||||
this.handlePasskeyResponse(trackingKey, { error: String(error) });
|
||||
}
|
||||
// Fallback to requestId for backward compatibility
|
||||
else if (requestId) {
|
||||
this.handlePasskeyResponse(requestId, { error: String(error) });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private handlePasskeyResponse(trackingKey: string, response: any): void {
|
||||
this.logService.info("Received passkey response for tracking key:", trackingKey, response);
|
||||
|
||||
if (!trackingKey) {
|
||||
this.logService.error("Response missing tracking key:", response);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logService.info(`Looking for pending request with tracking key: ${trackingKey}`);
|
||||
this.logService.info(
|
||||
`Current pending requests: ${Array.from(this.pendingPasskeyRequests.keys())}`,
|
||||
);
|
||||
|
||||
const resolver = this.pendingPasskeyRequests.get(trackingKey);
|
||||
if (resolver) {
|
||||
this.logService.info("Found resolver, calling with response data:", response);
|
||||
resolver(response);
|
||||
} else {
|
||||
this.logService.warning("No pending request found for tracking key:", trackingKey);
|
||||
}
|
||||
}
|
||||
|
||||
private async runCommand<C extends CommandDefinition>(
|
||||
command: RunCommandParams<C>,
|
||||
): Promise<RunCommandResult<C>> {
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { autofill, passkey_authenticator } from "@bitwarden/desktop-napi";
|
||||
|
||||
import { WindowMain } from "../../../main/window.main";
|
||||
|
||||
import { NativeAutofillFido2Credential, NativeAutofillSyncParams } from "./sync.command";
|
||||
|
||||
export class NativeAutofillWindowsMain {
|
||||
private pendingPasskeyRequests = new Map<string, (response: any) => void>();
|
||||
|
||||
constructor(
|
||||
private logService: LogService,
|
||||
private windowMain: WindowMain,
|
||||
) {}
|
||||
|
||||
initWindows() {
|
||||
passkey_authenticator.register();
|
||||
void passkey_authenticator.onRequest(async (error, event) => {
|
||||
this.logService.info("Passkey request received:", { error, event });
|
||||
|
||||
try {
|
||||
const request = JSON.parse(event.requestJson);
|
||||
this.logService.info("Parsed passkey request:", { type: event.requestType, request });
|
||||
|
||||
// Handle different request types based on the requestType field
|
||||
switch (event.requestType) {
|
||||
case "assertion":
|
||||
return await this.handleAssertionRequest(request);
|
||||
case "registration":
|
||||
return await this.handleRegistrationRequest(request);
|
||||
case "sync":
|
||||
return await this.handleSyncRequest(request);
|
||||
default:
|
||||
this.logService.error("Unknown passkey request type:", event.requestType);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: `Unknown request type: ${event.requestType}`,
|
||||
});
|
||||
}
|
||||
} catch (parseError) {
|
||||
this.logService.error("Failed to parse passkey request:", parseError);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: "Failed to parse request JSON",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async handleAssertionRequest(request: autofill.PasskeyAssertionRequest): Promise<string> {
|
||||
this.logService.info("Handling assertion request for rpId:", request.rpId);
|
||||
|
||||
try {
|
||||
// Generate unique identifiers for tracking this request
|
||||
const clientId = Date.now();
|
||||
const sequenceNumber = Math.floor(Math.random() * 1000000);
|
||||
|
||||
// Send request and wait for response
|
||||
const response = await this.sendAndOptionallyWait<autofill.PasskeyAssertionResponse>(
|
||||
"autofill.passkeyAssertion",
|
||||
{
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
request: request,
|
||||
},
|
||||
{ waitForResponse: true, timeout: 60000 },
|
||||
);
|
||||
|
||||
if (response) {
|
||||
// Convert the response to the format expected by the NAPI bridge
|
||||
return JSON.stringify({
|
||||
type: "assertion_response",
|
||||
...response,
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: "No response received from renderer",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logService.error("Error in assertion request:", error);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: `Assertion request failed: ${error.message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleRegistrationRequest(
|
||||
request: autofill.PasskeyRegistrationRequest,
|
||||
): Promise<string> {
|
||||
this.logService.info("Handling registration request for rpId:", request.rpId);
|
||||
|
||||
try {
|
||||
// Generate unique identifiers for tracking this request
|
||||
const clientId = Date.now();
|
||||
const sequenceNumber = Math.floor(Math.random() * 1000000);
|
||||
|
||||
// Send request and wait for response
|
||||
const response = await this.sendAndOptionallyWait<autofill.PasskeyRegistrationResponse>(
|
||||
"autofill.passkeyRegistration",
|
||||
{
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
request: request,
|
||||
},
|
||||
{ waitForResponse: true, timeout: 60000 },
|
||||
);
|
||||
|
||||
this.logService.info("Received response for registration request:", response);
|
||||
|
||||
if (response) {
|
||||
// Convert the response to the format expected by the NAPI bridge
|
||||
return JSON.stringify({
|
||||
type: "registration_response",
|
||||
...response,
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: "No response received from renderer",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logService.error("Error in registration request:", error);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: `Registration request failed: ${error.message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSyncRequest(
|
||||
request: passkey_authenticator.PasskeySyncRequest,
|
||||
): Promise<string> {
|
||||
this.logService.info("Handling sync request for rpId:", request.rpId);
|
||||
|
||||
try {
|
||||
// Generate unique identifiers for tracking this request
|
||||
const clientId = Date.now();
|
||||
const sequenceNumber = Math.floor(Math.random() * 1000000);
|
||||
|
||||
// Send sync request and wait for response
|
||||
const response = await this.sendAndOptionallyWait<passkey_authenticator.PasskeySyncResponse>(
|
||||
"autofill.passkeySync",
|
||||
{
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
request: { rpId: request.rpId },
|
||||
},
|
||||
{ waitForResponse: true, timeout: 60000 },
|
||||
);
|
||||
|
||||
this.logService.info("Received response for sync request:", response);
|
||||
|
||||
if (response && response.credentials) {
|
||||
// Convert the response to the format expected by the NAPI bridge
|
||||
return JSON.stringify({
|
||||
type: "sync_response",
|
||||
credentials: response.credentials,
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: "No credentials received from renderer",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logService.error("Error in sync request:", error);
|
||||
return JSON.stringify({
|
||||
type: "error",
|
||||
message: `Sync request failed: ${error.message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for webContents.send that optionally waits for a response
|
||||
* @param channel The IPC channel to send to
|
||||
* @param data The data to send
|
||||
* @param options Optional configuration
|
||||
* @returns Promise that resolves with the response if waitForResponse is true
|
||||
*/
|
||||
private async sendAndOptionallyWait<T = any>(
|
||||
channel: string,
|
||||
data: any,
|
||||
options?: { waitForResponse?: boolean; timeout?: number },
|
||||
): Promise<T | void> {
|
||||
if (!options?.waitForResponse) {
|
||||
// Just send without waiting for response (existing behavior)
|
||||
this.logService.info(`Sending fire-and-forget message to ${channel}`);
|
||||
this.windowMain.win.webContents.send(channel, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use clientId and sequenceNumber as the tracking key
|
||||
const trackingKey = `${data.clientId}_${data.sequenceNumber}`;
|
||||
const timeout = options.timeout || 30000; // 30 second default timeout
|
||||
|
||||
// Send the original data without adding requestId
|
||||
const dataWithId = { ...data };
|
||||
|
||||
this.logService.info(`Sending awaitable request ${trackingKey} to ${channel}`, { dataWithId });
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
// Set up timeout
|
||||
const timeoutId = setTimeout(() => {
|
||||
this.logService.warning(`Request ${trackingKey} timed out after ${timeout}ms`);
|
||||
this.pendingPasskeyRequests.delete(trackingKey);
|
||||
reject(new Error(`Request timeout after ${timeout}ms`));
|
||||
}, timeout);
|
||||
|
||||
// Store the resolver
|
||||
this.pendingPasskeyRequests.set(trackingKey, (response: T) => {
|
||||
this.logService.info(`Request ${trackingKey} resolved with response:`, response);
|
||||
clearTimeout(timeoutId);
|
||||
this.pendingPasskeyRequests.delete(trackingKey);
|
||||
resolve(response);
|
||||
});
|
||||
|
||||
this.logService.info(
|
||||
`Stored resolver for request ${trackingKey}, total pending: ${this.pendingPasskeyRequests.size}`,
|
||||
);
|
||||
|
||||
// Send the request
|
||||
this.windowMain.win.webContents.send(channel, dataWithId);
|
||||
});
|
||||
}
|
||||
|
||||
setupWindowsRendererIPCHandlers() {
|
||||
ipcMain.handle(
|
||||
"autofill.syncPasskeys",
|
||||
async (event, data: NativeAutofillSyncParams): Promise<string> => {
|
||||
this.logService.info("autofill.syncPasskeys", data);
|
||||
const { credentials } = data;
|
||||
const mapped = credentials.map((cred: NativeAutofillFido2Credential) => {
|
||||
const x: passkey_authenticator.SyncedCredential = {
|
||||
credentialId: cred.credentialId,
|
||||
rpId: cred.rpId,
|
||||
userName: cred.userName,
|
||||
userHandle: cred.userHandle,
|
||||
};
|
||||
this.logService.info("Mapped credential:", x);
|
||||
return x;
|
||||
});
|
||||
|
||||
this.logService.info("Syncing passkeys to Windows:", mapped);
|
||||
|
||||
passkey_authenticator.syncCredentialsToWindows(mapped);
|
||||
|
||||
return "worked";
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.on("autofill.completePasskeySync", (event, data) => {
|
||||
this.logService.warning("autofill.completePasskeySync", data);
|
||||
const { clientId, sequenceNumber, response, requestId } = data;
|
||||
|
||||
// Handle awaitable passkey requests using clientId and sequenceNumber
|
||||
if (clientId !== undefined && sequenceNumber !== undefined) {
|
||||
const trackingKey = `${clientId}_${sequenceNumber}`;
|
||||
this.handlePasskeyResponse(trackingKey, response);
|
||||
}
|
||||
// Fallback to requestId for backward compatibility
|
||||
else if (requestId) {
|
||||
this.handlePasskeyResponse(requestId, response);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on("autofill.completePasskeyRegistration", (event, data) => {
|
||||
this.logService.warning("autofill.completePasskeyRegistration", data);
|
||||
const { clientId, sequenceNumber, response, requestId } = data;
|
||||
|
||||
// Handle awaitable passkey requests using clientId and sequenceNumber
|
||||
if (clientId !== undefined && sequenceNumber !== undefined) {
|
||||
const trackingKey = `${clientId}_${sequenceNumber}`;
|
||||
this.handlePasskeyResponse(trackingKey, response);
|
||||
}
|
||||
// Fallback to requestId for backward compatibility
|
||||
else if (requestId) {
|
||||
this.handlePasskeyResponse(requestId, response);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on("autofill.completePasskeyAssertion", (event, data) => {
|
||||
this.logService.warning("autofill.completePasskeyAssertion", data);
|
||||
const { clientId, sequenceNumber, response, requestId } = data;
|
||||
|
||||
// Handle awaitable passkey requests using clientId and sequenceNumber
|
||||
if (clientId !== undefined && sequenceNumber !== undefined) {
|
||||
const trackingKey = `${clientId}_${sequenceNumber}`;
|
||||
this.handlePasskeyResponse(trackingKey, response);
|
||||
}
|
||||
// Fallback to requestId for backward compatibility
|
||||
else if (requestId) {
|
||||
this.handlePasskeyResponse(requestId, response);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on("autofill.completeError", (event, data) => {
|
||||
this.logService.warning("autofill.completeError", data);
|
||||
const { clientId, sequenceNumber, error, requestId } = data;
|
||||
|
||||
// Handle awaitable passkey requests using clientId and sequenceNumber
|
||||
if (clientId !== undefined && sequenceNumber !== undefined) {
|
||||
const trackingKey = `${clientId}_${sequenceNumber}`;
|
||||
this.handlePasskeyResponse(trackingKey, { error: String(error) });
|
||||
}
|
||||
// Fallback to requestId for backward compatibility
|
||||
else if (requestId) {
|
||||
this.handlePasskeyResponse(requestId, { error: String(error) });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private handlePasskeyResponse(trackingKey: string, response: any): void {
|
||||
this.logService.info("Received passkey response for tracking key:", trackingKey, response);
|
||||
|
||||
if (!trackingKey) {
|
||||
this.logService.error("Response missing tracking key:", response);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logService.info(`Looking for pending request with tracking key: ${trackingKey}`);
|
||||
this.logService.info(
|
||||
`Current pending requests: ${Array.from(this.pendingPasskeyRequests.keys())}`,
|
||||
);
|
||||
|
||||
const resolver = this.pendingPasskeyRequests.get(trackingKey);
|
||||
if (resolver) {
|
||||
this.logService.info("Found resolver, calling with response data:", response);
|
||||
resolver(response);
|
||||
} else {
|
||||
this.logService.warning("No pending request found for tracking key:", trackingKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user