mirror of
https://github.com/bitwarden/browser
synced 2026-02-06 19:53:59 +00:00
Wire up client user verification over IPC channel
This commit is contained in:
12
apps/desktop/desktop_native/napi/index.d.ts
vendored
12
apps/desktop/desktop_native/napi/index.d.ts
vendored
@@ -18,7 +18,11 @@ export declare namespace autofill {
|
||||
completeAssertion(clientId: number, sequenceNumber: number, response: PasskeyAssertionResponse): number
|
||||
completeWindowHandleQuery(clientId: number, sequenceNumber: number, response: WindowHandleQueryResponse): number
|
||||
completeError(clientId: number, sequenceNumber: number, error: string): number
|
||||
/** Prompt a user for user verification using OS APIs. */
|
||||
verifyUser(request: UserVerificationRequest): Promise<UserVerificationResponse>
|
||||
}
|
||||
export type HostResponse =
|
||||
| { type: 'UserVerification', field0: UserVerificationResponse }
|
||||
export interface NativeStatus {
|
||||
key: string
|
||||
value: string
|
||||
@@ -80,6 +84,14 @@ export declare namespace autofill {
|
||||
Required = 'required',
|
||||
Discouraged = 'discouraged'
|
||||
}
|
||||
export interface UserVerificationRequest {
|
||||
transactionId: number
|
||||
displayHint: string
|
||||
username?: string
|
||||
}
|
||||
export interface UserVerificationResponse {
|
||||
userVerified: boolean
|
||||
}
|
||||
export interface WindowHandleQueryRequest {
|
||||
windowHandle: string
|
||||
}
|
||||
|
||||
@@ -635,6 +635,11 @@ pub mod autostart {
|
||||
|
||||
#[napi]
|
||||
pub mod autofill {
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{atomic::AtomicU32, Arc, Mutex},
|
||||
};
|
||||
|
||||
use desktop_core::ipc::server::{Message, MessageType};
|
||||
use napi::{
|
||||
bindgen_prelude::FnArgs,
|
||||
@@ -793,6 +798,27 @@ pub mod autofill {
|
||||
pub credential_id: Vec<u8>,
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UserVerificationRequest {
|
||||
pub transaction_id: u32,
|
||||
pub display_hint: String,
|
||||
pub username: Option<String>,
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UserVerificationResponse {
|
||||
pub user_verified: bool,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub enum HostResponse {
|
||||
UserVerification(UserVerificationResponse),
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub struct AutofillIpcServer {
|
||||
server: desktop_core::ipc::server::Server,
|
||||
@@ -1040,6 +1066,42 @@ pub mod autofill {
|
||||
self.send(client_id, serde_json::to_string(&message).unwrap())
|
||||
}
|
||||
|
||||
/// Prompt a user for user verification using OS APIs.
|
||||
#[napi]
|
||||
pub async fn verify_user(
|
||||
&self,
|
||||
request: UserVerificationRequest,
|
||||
) -> napi::Result<UserVerificationResponse> {
|
||||
let request_id = self
|
||||
.host_callbacks_counter
|
||||
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let command = HostRequestMessage {
|
||||
request_id,
|
||||
request: HostRequest::UserVerification(request),
|
||||
};
|
||||
self.host_callbacks
|
||||
.lock()
|
||||
.expect("not poisoned")
|
||||
.insert(request_id, tx);
|
||||
let json = serde_json::to_string(&command).expect("serde to serialize");
|
||||
tracing::debug!(json, "Sending verify user message");
|
||||
self.send(0, json)?;
|
||||
|
||||
match rx.await {
|
||||
Ok(Ok(HostResponse::UserVerification(response))) => Ok(response),
|
||||
Ok(Ok(_)) => Err(napi::Error::from_reason(
|
||||
"Invalid repsonse received for user verification request",
|
||||
)),
|
||||
Ok(Err(err)) => Err(napi::Error::from_reason(format!(
|
||||
"UV request failed: {err}"
|
||||
))),
|
||||
Err(err) => Err(napi::Error::from_reason(format!(
|
||||
"Failed to receive UV request: {err}"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add a way to send a message to a specific client?
|
||||
fn send(&self, _client_id: u32, message: String) -> napi::Result<u32> {
|
||||
self.server
|
||||
|
||||
@@ -14,6 +14,9 @@ export default {
|
||||
runCommand: <C extends Command>(params: RunCommandParams<C>): Promise<RunCommandResult<C>> =>
|
||||
ipcRenderer.invoke("autofill.runCommand", params),
|
||||
|
||||
verifyUser: (request: autofill.UserVerificationRequest): Promise<autofill.UserVerificationResponse> =>
|
||||
ipcRenderer.invoke("autofill.userVerification", request),
|
||||
|
||||
listenerReady: () => ipcRenderer.send("autofill.listenerReady"),
|
||||
|
||||
listenPasskeyRegistration: (
|
||||
|
||||
@@ -32,7 +32,7 @@ import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view
|
||||
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
||||
import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view";
|
||||
|
||||
import { NativeAutofillUserVerificationCommand } from "../../platform/main/autofill/user-verification.command";
|
||||
import { NativeAutofillUserVerificationRequest } from "../../platform/main/autofill/user-verification.request";
|
||||
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
|
||||
import { CipherViewLikeUtils } from "@bitwarden/common/vault/utils/cipher-view-like-utils";
|
||||
|
||||
@@ -421,21 +421,19 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
|
||||
this.logService.debug("Prompting for user verification");
|
||||
|
||||
const uvResult = await ipc.autofill.runCommand<NativeAutofillUserVerificationCommand>({
|
||||
namespace: "autofill",
|
||||
command: "user-verification",
|
||||
params: {
|
||||
windowHandle: Utils.fromBufferToB64(windowHandle),
|
||||
transactionContext: this.transactionContext,
|
||||
try {
|
||||
const uvResult = await ipc.autofill.verifyUser({
|
||||
// windowHandle: Utils.fromBufferToB64(windowHandle),
|
||||
// transactionContext: this.transactionContext,
|
||||
transactionId: 0,
|
||||
username,
|
||||
displayHint,
|
||||
},
|
||||
});
|
||||
if (uvResult.type === "error") {
|
||||
this.logService.error("Error getting user verification", uvResult.error);
|
||||
});
|
||||
return uvResult.userVerified;
|
||||
} catch (err) {
|
||||
this.logService.error("Error getting user verification", err);
|
||||
return false;
|
||||
}
|
||||
return uvResult.type === "success";
|
||||
}
|
||||
|
||||
async informExcludedCredential(existingCipherIds: string[]): Promise<void> {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { NativeAutofillStatusCommand } from "./status.command";
|
||||
import { NativeAutofillSyncCommand } from "./sync.command";
|
||||
import { NativeAutofillUserVerificationCommand } from "./user-verification.command";
|
||||
|
||||
export type CommandDefinition = {
|
||||
namespace: string;
|
||||
@@ -21,4 +20,4 @@ export type IpcCommandInvoker<C extends CommandDefinition> = (
|
||||
) => Promise<CommandOutput<C["output"]>>;
|
||||
|
||||
/** A list of all available commands */
|
||||
export type Command = NativeAutofillSyncCommand | NativeAutofillStatusCommand | NativeAutofillUserVerificationCommand;
|
||||
export type Command = NativeAutofillSyncCommand | NativeAutofillStatusCommand;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { autofill } from "@bitwarden/desktop-napi";
|
||||
import { WindowMain } from "../../../main/window.main";
|
||||
|
||||
import { CommandDefinition } from "./command";
|
||||
import { HostRequestDefinition } from "./request";
|
||||
|
||||
type BufferedMessage = {
|
||||
channel: string;
|
||||
@@ -20,6 +21,14 @@ export type RunCommandParams<C extends CommandDefinition> = {
|
||||
|
||||
export type RunCommandResult<C extends CommandDefinition> = C["output"];
|
||||
|
||||
export type HostRequestParams<R extends HostRequestDefinition> = {
|
||||
namespace: R["namespace"];
|
||||
command: R["name"];
|
||||
params: R["input"];
|
||||
};
|
||||
|
||||
export type HostRequestResult<R extends HostRequestDefinition> = R["output"];
|
||||
|
||||
export class NativeAutofillMain {
|
||||
private ipcServer?: autofill.AutofillIpcServer;
|
||||
private messageBuffer: BufferedMessage[] = [];
|
||||
@@ -70,6 +79,15 @@ export class NativeAutofillMain {
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
"autofill.userVerification",
|
||||
(
|
||||
_event: any,
|
||||
params: autofill.UserVerificationRequest,
|
||||
): Promise<autofill.UserVerificationResponse> =>{
|
||||
return this.ipcServer.verifyUser(params)
|
||||
});
|
||||
|
||||
this.ipcServer = await autofill.AutofillIpcServer.listen(
|
||||
"af",
|
||||
// RegistrationCallback
|
||||
|
||||
14
apps/desktop/src/platform/main/autofill/request.ts
Normal file
14
apps/desktop/src/platform/main/autofill/request.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Contains types for request/response messages from the host app to the provider over IPC.
|
||||
*/
|
||||
import { NativeAutofillUserVerificationRequest } from "./user-verification.request";
|
||||
|
||||
export type HostRequestDefinition = {
|
||||
namespace: string;
|
||||
name: string;
|
||||
input: Record<string, unknown>;
|
||||
output: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** A list of all available host requests */
|
||||
export type HostRequest = NativeAutofillUserVerificationRequest;
|
||||
@@ -1,19 +0,0 @@
|
||||
import { CommandDefinition, CommandOutput } from "./command";
|
||||
|
||||
export interface NativeAutofillUserVerificationCommand extends CommandDefinition {
|
||||
name: "user-verification";
|
||||
input: NativeAutofillUserVerificationParams;
|
||||
output: NativeAutofillUserVerificationResult;
|
||||
}
|
||||
|
||||
export type NativeAutofillUserVerificationParams = {
|
||||
/** base64 string representing native window handle */
|
||||
windowHandle: string;
|
||||
/** base64 string representing native transaction context */
|
||||
transactionContext: string;
|
||||
displayHint: string;
|
||||
username: string;
|
||||
};
|
||||
|
||||
|
||||
export type NativeAutofillUserVerificationResult = CommandOutput<{}>;
|
||||
@@ -0,0 +1,21 @@
|
||||
import { HostRequestDefinition } from "./request";
|
||||
|
||||
export interface NativeAutofillUserVerificationRequest extends HostRequestDefinition {
|
||||
name: "user-verification";
|
||||
input: NativeAutofillUserVerificationParams;
|
||||
output: NativeAutofillUserVerificationResult;
|
||||
}
|
||||
|
||||
export type NativeAutofillUserVerificationParams = {
|
||||
// /** base64 string representing native window handle */
|
||||
// windowHandle: string;
|
||||
// /** base64 string representing native transaction context */
|
||||
transactionId: number;
|
||||
displayHint: string;
|
||||
username: string;
|
||||
};
|
||||
|
||||
export type NativeAutofillUserVerificationResult = {
|
||||
/** Whether the user was verified. */
|
||||
userVerified: boolean
|
||||
}
|
||||
Reference in New Issue
Block a user