1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-09 21:20:27 +00:00

Use a string for transaction context everywhere

This commit is contained in:
Isaiah Inuwa
2025-11-25 07:40:37 -06:00
parent 46f990c340
commit 7e251f8abb
13 changed files with 52 additions and 62 deletions

View File

@@ -165,7 +165,7 @@ export declare namespace autofill {
supportedAlgorithms: Array<number>
windowXy: Position
excludedCredentials: Array<Array<number>>
context?: Array<number>
context?: string
}
export interface PasskeyRegistrationResponse {
rpId: string
@@ -179,7 +179,7 @@ export declare namespace autofill {
userVerification: UserVerification
allowedCredentials: Array<Array<number>>
windowXy: Position
context?: Array<number>
context?: string
}
export interface PasskeyAssertionWithoutUserInterfaceRequest {
rpId: string
@@ -190,7 +190,7 @@ export declare namespace autofill {
clientDataHash: Array<number>
userVerification: UserVerification
windowXy: Position
context?: Array<number>
context?: string
}
export interface NativeStatus {
key: string

View File

@@ -695,7 +695,7 @@ pub mod autofill {
pub supported_algorithms: Vec<i32>,
pub window_xy: Position,
pub excluded_credentials: Vec<Vec<u8>>,
pub context: Option<Vec<u8>>,
pub context: Option<String>,
}
#[napi(object)]
@@ -717,7 +717,7 @@ pub mod autofill {
pub user_verification: UserVerification,
pub allowed_credentials: Vec<Vec<u8>>,
pub window_xy: Position,
pub context: Option<Vec<u8>>,
pub context: Option<String>,
//extension_input: Vec<u8>, TODO: Implement support for extensions
}
@@ -733,7 +733,7 @@ pub mod autofill {
pub client_data_hash: Vec<u8>,
pub user_verification: UserVerification,
pub window_xy: Position,
pub context: Option<Vec<u8>>,
pub context: Option<String>,
}
#[napi(object)]

View File

@@ -12,7 +12,7 @@ use crate::{
PasskeyAssertionWithoutUserInterfaceRequest, Position, TimedCallback, UserVerification,
WindowsProviderClient,
},
util::HwndExt,
util::{create_context_string, HwndExt},
};
pub fn get_assertion(
@@ -44,7 +44,6 @@ pub fn get_assertion(
.map(|id| id.to_vec())
.collect();
let transaction_id = request.transaction_id.to_u128().to_le_bytes().to_vec();
let client_pos = request
.window_handle
.center_position()
@@ -55,6 +54,7 @@ pub fn get_assertion(
rp_id,
allowed_credential_ids
);
let context = create_context_string(request.transaction_id);
// Send assertion request
let assertion_request = PasskeyAssertionRequest {
@@ -66,7 +66,7 @@ pub fn get_assertion(
x: client_pos.0,
y: client_pos.1,
},
context: transaction_id,
context,
};
let passkey_response =
send_assertion_request(ipc_client, assertion_request, cancellation_token)
@@ -107,9 +107,6 @@ fn send_assertion_request(
let request = PasskeyAssertionWithoutUserInterfaceRequest {
rp_id: request.rp_id,
credential_id: request.allowed_credentials[0].clone(),
// user_name: request.user_name,
// user_handle: request.,
// record_identifier: todo!(),
client_data_hash: request.client_data_hash,
user_verification: request.user_verification,
window_xy: request.window_xy,

View File

@@ -12,7 +12,7 @@ pub struct PasskeyAssertionRequest {
pub user_verification: UserVerification,
pub allowed_credentials: Vec<Vec<u8>>,
pub window_xy: Position,
pub context: Vec<u8>,
pub context: String,
// pub extension_input: Vec<u8>, TODO: Implement support for extensions
}
@@ -21,13 +21,10 @@ pub struct PasskeyAssertionRequest {
pub struct PasskeyAssertionWithoutUserInterfaceRequest {
pub rp_id: String,
pub credential_id: Vec<u8>,
// pub user_name: String,
// pub user_handle: Vec<u8>,
// pub record_identifier: Option<String>,
pub client_data_hash: Vec<u8>,
pub user_verification: UserVerification,
pub window_xy: Position,
pub context: Vec<u8>,
pub context: String,
}
#[derive(Debug, Serialize, Deserialize)]

View File

@@ -4,7 +4,7 @@ use std::{
fmt::Display,
sync::{
atomic::AtomicU32,
mpsc::{self, Receiver, RecvError, RecvTimeoutError, Sender},
mpsc::{self, Receiver, RecvTimeoutError, Sender},
Arc, Mutex,
},
time::{Duration, Instant},

View File

@@ -15,7 +15,7 @@ pub struct PasskeyRegistrationRequest {
pub supported_algorithms: Vec<i32>,
pub window_xy: Position,
pub excluded_credentials: Vec<Vec<u8>>,
pub context: Vec<u8>,
pub context: String,
}
#[derive(Debug, Serialize, Deserialize)]

View File

@@ -1,6 +1,5 @@
use serde_json;
use std::collections::HashMap;
use std::sync::mpsc::TryRecvError;
use std::sync::{mpsc::Receiver, Arc};
use std::time::Duration;
@@ -10,6 +9,7 @@ use win_webauthn::{
};
use crate::ipc2::CallbackError;
use crate::util::create_context_string;
use crate::{
ipc2::{
PasskeyRegistrationRequest, PasskeyRegistrationResponse, Position, TimedCallback,
@@ -87,12 +87,13 @@ pub fn make_credential(
);
}
let transaction_id = request.transaction_id.to_u128().to_le_bytes().to_vec();
let client_pos = request
.window_handle
.center_position()
.unwrap_or((640, 480));
let context = create_context_string(request.transaction_id);
// Create Windows registration request
let registration_request = PasskeyRegistrationRequest {
rp_id: rpid.clone(),
@@ -107,7 +108,7 @@ pub fn make_credential(
x: client_pos.0,
y: client_pos.1,
},
context: transaction_id,
context,
};
tracing::debug!(

View File

@@ -1,5 +1,11 @@
use windows::Win32::UI::HiDpi::GetDpiForWindow;
use windows::Win32::{Foundation::*, UI::WindowsAndMessaging::GetWindowRect};
use base64::engine::{general_purpose::STANDARD, Engine as _};
use windows::{
core::GUID,
Win32::{
Foundation::*,
UI::{HiDpi::GetDpiForWindow, WindowsAndMessaging::GetWindowRect},
},
};
const BASE_DPI: u32 = 96;
@@ -34,3 +40,7 @@ impl HwndExt for HWND {
}
}
}
pub fn create_context_string(transaction_id: GUID) -> String {
STANDARD.encode(transaction_id.to_u128().to_le_bytes().to_vec())
}

View File

@@ -211,20 +211,16 @@ export class DesktopAutofillService implements OnDestroy {
this.logService.debug("listenPasskeyRegistration2", this.convertRegistrationRequest(request));
const controller = new AbortController();
let requestId = request.context ? this.contextToRequestId(request.context) : null;
this.logService.debug("Request context:", requestId)
if (requestId) {
this.inFlightRequests[requestId] = controller;
if (request.context) {
this.inFlightRequests[request.context] = controller;
}
const ctx = request.context ? new Uint8Array(request.context).buffer : null;
try {
const response = await this.fido2AuthenticatorService.makeCredential(
this.convertRegistrationRequest(request),
{ windowXy: request.windowXy },
controller,
ctx
request.context,
);
this.logService.debug("Sending registration response to plugin via callback");
@@ -234,8 +230,8 @@ export class DesktopAutofillService implements OnDestroy {
callback(error, null);
}
finally {
if (requestId) {
delete this.inFlightRequests[requestId];
if (request.context) {
delete this.inFlightRequests[request.context];
}
}
this.logService.info("Passkey registration completed.")
@@ -259,9 +255,8 @@ export class DesktopAutofillService implements OnDestroy {
);
const controller = new AbortController();
let requestId = request.context ? this.contextToRequestId(request.context) : null;
if (requestId) {
this.inFlightRequests[requestId] = controller;
if (request.context) {
this.inFlightRequests[request.context] = controller;
}
try {
@@ -297,13 +292,12 @@ export class DesktopAutofillService implements OnDestroy {
new Uint8Array(parseCredentialId(decrypted.login.fido2Credentials?.[0].credentialId)),
);
}
const ctx = request.context ? new Uint8Array(request.context).buffer : null;
const response = await this.fido2AuthenticatorService.getAssertion(
this.convertAssertionRequest(request, true),
{ windowXy: request.windowXy },
controller,
ctx
request.context
);
callback(null, this.convertAssertionResponse(request, response));
@@ -313,8 +307,8 @@ export class DesktopAutofillService implements OnDestroy {
return;
}
finally {
if (requestId) {
delete this.inFlightRequests[requestId];
if (request.context) {
delete this.inFlightRequests[request.context];
}
}
},
@@ -330,11 +324,9 @@ export class DesktopAutofillService implements OnDestroy {
}
this.logService.debug("listenPasskeyAssertion", clientId, sequenceNumber, request);
const ctx = request.context ? new Uint8Array(request.context).buffer : null;
const controller = new AbortController();
let requestId = request.context ? this.contextToRequestId(request.context) : null;
if (requestId) {
this.inFlightRequests[requestId] = controller;
if (request.context) {
this.inFlightRequests[request.context] = controller;
}
try {
@@ -342,7 +334,7 @@ export class DesktopAutofillService implements OnDestroy {
this.convertAssertionRequest(request),
{ windowXy: request.windowXy },
controller,
ctx
request.context,
);
callback(null, this.convertAssertionResponse(request, response));
@@ -351,8 +343,8 @@ export class DesktopAutofillService implements OnDestroy {
callback(error, null);
}
finally {
if (requestId) {
delete this.inFlightRequests[requestId];
if (request.context) {
delete this.inFlightRequests[request.context];
}
}
});
@@ -500,12 +492,6 @@ export class DesktopAutofillService implements OnDestroy {
};
}
private contextToRequestId(context: number[]): string {
const buf = new Uint8Array(context).buffer;
const requestId = Utils.fromBufferToB64(buf);
return requestId
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();

View File

@@ -67,7 +67,7 @@ export class DesktopFido2UserInterfaceService
fallbackSupported: boolean,
nativeWindowObject: NativeWindowObject,
abortController?: AbortController,
transactionContext?: ArrayBuffer,
transactionContext?: string,
): Promise<DesktopFido2UserInterfaceSession> {
this.logService.debug("newSession", fallbackSupported, abortController, nativeWindowObject, transactionContext);
const session = new DesktopFido2UserInterfaceSession(
@@ -97,7 +97,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
private desktopSettingsService: DesktopSettingsService,
private windowObject: NativeWindowObject,
private abortController: AbortController,
private transactionContext: ArrayBuffer,
private transactionContext: string,
) {}
private confirmCredentialSubject = new Subject<boolean>();
@@ -352,7 +352,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
command: "user-verification",
params: {
windowHandle: Utils.fromBufferToB64(windowHandle),
transactionContext: Utils.fromBufferToB64(this.transactionContext),
transactionContext: this.transactionContext,
username,
displayHint,
},

View File

@@ -19,7 +19,7 @@ export abstract class Fido2AuthenticatorService<ParentWindowReference> {
params: Fido2AuthenticatorMakeCredentialsParams,
window: ParentWindowReference,
abortController?: AbortController,
transactionContext?: ArrayBuffer,
transactionContext?: string,
): Promise<Fido2AuthenticatorMakeCredentialResult>;
/**
@@ -34,7 +34,7 @@ export abstract class Fido2AuthenticatorService<ParentWindowReference> {
params: Fido2AuthenticatorGetAssertionParams,
window: ParentWindowReference,
abortController?: AbortController,
transactionContext?: ArrayBuffer,
transactionContext?: string,
): Promise<Fido2AuthenticatorGetAssertionResult>;
/**

View File

@@ -71,7 +71,7 @@ export abstract class Fido2UserInterfaceService<ParentWindowReference> {
fallbackSupported: boolean,
window: ParentWindowReference,
abortController?: AbortController,
transactionContext?: ArrayBuffer,
transactionContext?: string,
): Promise<Fido2UserInterfaceSession>;
}
@@ -91,7 +91,6 @@ export abstract class Fido2UserInterfaceSession {
* Ask the user to confirm the creation of a new credential.
*
* @param params The parameters to use when asking the user to confirm the creation of a new credential.
* @param abortController An abort controller that can be used to cancel/close the session.
* @returns The ID of the cipher where the new credential should be saved.
*/
abstract confirmNewCredential(

View File

@@ -61,7 +61,7 @@ export class Fido2AuthenticatorService<ParentWindowReference>
params: Fido2AuthenticatorMakeCredentialsParams,
window: ParentWindowReference,
abortController?: AbortController,
transactionContext?: ArrayBuffer,
transactionContext?: string,
): Promise<Fido2AuthenticatorMakeCredentialResult> {
const userInterfaceSession = await this.userInterface.newSession(
params.fallbackSupported,
@@ -232,7 +232,7 @@ export class Fido2AuthenticatorService<ParentWindowReference>
params: Fido2AuthenticatorGetAssertionParams,
window: ParentWindowReference,
abortController?: AbortController,
transactionContext?: ArrayBuffer,
transactionContext?: string,
): Promise<Fido2AuthenticatorGetAssertionResult> {
const userInterfaceSession = await this.userInterface.newSession(
params.fallbackSupported,