mirror of
https://github.com/bitwarden/browser
synced 2026-02-25 17:13:24 +00:00
Use WebAuthn client window for silent assertions
This commit is contained in:
3
apps/desktop/desktop_native/napi/index.d.ts
vendored
3
apps/desktop/desktop_native/napi/index.d.ts
vendored
@@ -165,6 +165,7 @@ export declare namespace autofill {
|
||||
supportedAlgorithms: Array<number>
|
||||
windowXy: Position
|
||||
excludedCredentials: Array<Array<number>>
|
||||
clientWindowHandle?: Array<number>
|
||||
context?: string
|
||||
}
|
||||
export interface PasskeyRegistrationResponse {
|
||||
@@ -179,6 +180,7 @@ export declare namespace autofill {
|
||||
userVerification: UserVerification
|
||||
allowedCredentials: Array<Array<number>>
|
||||
windowXy: Position
|
||||
clientWindowHandle?: Array<number>
|
||||
context?: string
|
||||
}
|
||||
export interface PasskeyAssertionWithoutUserInterfaceRequest {
|
||||
@@ -190,6 +192,7 @@ export declare namespace autofill {
|
||||
clientDataHash: Array<number>
|
||||
userVerification: UserVerification
|
||||
windowXy: Position
|
||||
clientWindowHandle?: Array<number>
|
||||
context?: string
|
||||
}
|
||||
export interface NativeStatus {
|
||||
|
||||
@@ -695,6 +695,7 @@ pub mod autofill {
|
||||
pub supported_algorithms: Vec<i32>,
|
||||
pub window_xy: Position,
|
||||
pub excluded_credentials: Vec<Vec<u8>>,
|
||||
pub client_window_handle: Option<Vec<u8>>,
|
||||
pub context: Option<String>,
|
||||
}
|
||||
|
||||
@@ -717,6 +718,7 @@ pub mod autofill {
|
||||
pub user_verification: UserVerification,
|
||||
pub allowed_credentials: Vec<Vec<u8>>,
|
||||
pub window_xy: Position,
|
||||
pub client_window_handle: Option<Vec<u8>>,
|
||||
pub context: Option<String>,
|
||||
//extension_input: Vec<u8>, TODO: Implement support for extensions
|
||||
}
|
||||
@@ -733,6 +735,7 @@ pub mod autofill {
|
||||
pub client_data_hash: Vec<u8>,
|
||||
pub user_verification: UserVerification,
|
||||
pub window_xy: Position,
|
||||
pub client_window_handle: Option<Vec<u8>>,
|
||||
pub context: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ pub fn get_assertion(
|
||||
.map(|id| id.to_vec())
|
||||
.collect();
|
||||
|
||||
let client_window_handle = request.window_handle.0.addr().to_le_bytes().to_vec();
|
||||
let client_pos = request
|
||||
.window_handle
|
||||
.center_position()
|
||||
@@ -62,6 +63,7 @@ pub fn get_assertion(
|
||||
client_data_hash,
|
||||
allowed_credentials: allowed_credential_ids,
|
||||
user_verification,
|
||||
client_window_handle,
|
||||
window_xy: Position {
|
||||
x: client_pos.0,
|
||||
y: client_pos.1,
|
||||
@@ -109,6 +111,7 @@ fn send_assertion_request(
|
||||
credential_id: request.allowed_credentials[0].clone(),
|
||||
client_data_hash: request.client_data_hash,
|
||||
user_verification: request.user_verification,
|
||||
client_window_handle: request.client_window_handle,
|
||||
window_xy: request.window_xy,
|
||||
context: request.context,
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ pub struct PasskeyAssertionRequest {
|
||||
pub user_verification: UserVerification,
|
||||
pub allowed_credentials: Vec<Vec<u8>>,
|
||||
pub window_xy: Position,
|
||||
pub client_window_handle: Vec<u8>,
|
||||
pub context: String,
|
||||
// pub extension_input: Vec<u8>, TODO: Implement support for extensions
|
||||
}
|
||||
@@ -24,6 +25,7 @@ pub struct PasskeyAssertionWithoutUserInterfaceRequest {
|
||||
pub client_data_hash: Vec<u8>,
|
||||
pub user_verification: UserVerification,
|
||||
pub window_xy: Position,
|
||||
pub client_window_handle: Vec<u8>,
|
||||
pub context: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ pub struct PasskeyRegistrationRequest {
|
||||
pub user_verification: UserVerification,
|
||||
pub supported_algorithms: Vec<i32>,
|
||||
pub window_xy: Position,
|
||||
pub client_window_handle: Vec<u8>,
|
||||
pub excluded_credentials: Vec<Vec<u8>>,
|
||||
pub context: String,
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ pub fn make_credential(
|
||||
);
|
||||
}
|
||||
|
||||
let client_window_handle = request.window_handle.0.addr().to_le_bytes().to_vec();
|
||||
let client_pos = request
|
||||
.window_handle
|
||||
.center_position()
|
||||
@@ -104,6 +105,7 @@ pub fn make_credential(
|
||||
excluded_credentials,
|
||||
user_verification: user_verification,
|
||||
supported_algorithms,
|
||||
client_window_handle,
|
||||
window_xy: Position {
|
||||
x: client_pos.0,
|
||||
y: client_pos.1,
|
||||
|
||||
@@ -213,7 +213,7 @@ export class Fido2CreateComponent implements OnInit, OnDestroy {
|
||||
|
||||
let cred = cipher.login.fido2Credentials[0];
|
||||
const username = cred.userName ?? cred.userDisplayName
|
||||
return this.session.promptForUserVerification(username, "Verify it's you to update a new credential")
|
||||
return this.session.promptForUserVerification(username, "Verify it's you to create a new credential")
|
||||
}
|
||||
|
||||
private async showErrorDialog(config: SimpleDialogOptions): Promise<void> {
|
||||
|
||||
@@ -215,10 +215,11 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
this.inFlightRequests[request.context] = controller;
|
||||
}
|
||||
|
||||
const clientHandle = request.clientWindowHandle ? new Uint8Array(request.clientWindowHandle) : null;
|
||||
try {
|
||||
const response = await this.fido2AuthenticatorService.makeCredential(
|
||||
this.convertRegistrationRequest(request),
|
||||
{ windowXy: request.windowXy },
|
||||
{ windowXy: request.windowXy, handle: clientHandle },
|
||||
controller,
|
||||
request.context,
|
||||
);
|
||||
@@ -293,9 +294,10 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
const clientHandle = request.clientWindowHandle ? new Uint8Array(request.clientWindowHandle) : null;
|
||||
const response = await this.fido2AuthenticatorService.getAssertion(
|
||||
this.convertAssertionRequest(request, true),
|
||||
{ windowXy: request.windowXy },
|
||||
{ windowXy: request.windowXy, handle: clientHandle },
|
||||
controller,
|
||||
request.context
|
||||
);
|
||||
@@ -329,10 +331,11 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
this.inFlightRequests[request.context] = controller;
|
||||
}
|
||||
|
||||
const clientHandle = request.clientWindowHandle ? new Uint8Array(request.clientWindowHandle) : null;
|
||||
try {
|
||||
const response = await this.fido2AuthenticatorService.getAssertion(
|
||||
this.convertAssertionRequest(request),
|
||||
{ windowXy: request.windowXy },
|
||||
{ windowXy: request.windowXy, handle: clientHandle },
|
||||
controller,
|
||||
request.context,
|
||||
);
|
||||
|
||||
@@ -43,6 +43,7 @@ export type NativeWindowObject = {
|
||||
* The position of the window, first entry is the x position, second is the y position
|
||||
*/
|
||||
windowXy?: { x: number; y: number };
|
||||
handle?: Uint8Array;
|
||||
};
|
||||
|
||||
export class DesktopFido2UserInterfaceService
|
||||
@@ -153,7 +154,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
const username = cred.userName ?? cred.userDisplayName
|
||||
// TODO: internationalization
|
||||
try {
|
||||
const isConfirmed = await this.promptForUserVerification(username, "Verify it's you to log in with Bitwarden.");
|
||||
const isConfirmed = await this.promptForUserVerification(username, "Verify it's you to log in with Bitwarden.", this.windowObject.handle);
|
||||
return { cipherId: cipherIds[0], userVerified: isConfirmed };
|
||||
}
|
||||
catch (e) {
|
||||
@@ -161,7 +162,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.logService.debug(
|
||||
this.logService.warning(
|
||||
"shortcut - Assuming user presence and returning cipherId",
|
||||
cipherIds[0],
|
||||
);
|
||||
@@ -371,9 +372,12 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
}
|
||||
|
||||
/** Called by the UI to prompt the user for verification. May be fulfilled by the OS. */
|
||||
async promptForUserVerification(username: string, displayHint: string): Promise<boolean> {
|
||||
async promptForUserVerification(username: string, displayHint: string, clientWindowHandle?: Uint8Array): Promise<boolean> {
|
||||
this.logService.info("DesktopFido2UserInterfaceSession] Prompting for user verification")
|
||||
let windowHandle = await ipc.platform.getNativeWindowHandle();
|
||||
// Use the client window handle if we're not showing UI; otherwise use our modal window.
|
||||
// For Windows, if the selected window handle is not in the foreground, then the Windows
|
||||
// Hello dialog will also be in the background.
|
||||
let windowHandle = clientWindowHandle ?? await ipc.platform.getNativeWindowHandle();
|
||||
|
||||
const uvResult = await ipc.autofill.runCommand<NativeAutofillUserVerificationCommand>({
|
||||
namespace: "autofill",
|
||||
|
||||
Reference in New Issue
Block a user