1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-29 15:53:45 +00:00

Throw if silent assert request requires showing UI

This commit is contained in:
Isaiah Inuwa
2026-01-12 13:52:46 -06:00
parent c1c8ff295b
commit 9f0ae4b799
5 changed files with 38 additions and 11 deletions

View File

@@ -307,7 +307,7 @@ export class DesktopAutofillService implements OnDestroy {
}
const response = await this.fido2AuthenticatorService.getAssertion(
this.convertAssertionRequest(request, true),
this.convertAssertionRequest(request, { assumeUserPresence: true, isSilent: true }),
{ windowXy: request.windowXy, handle: clientHandle },
controller,
request.context,
@@ -453,15 +453,20 @@ export class DesktopAutofillService implements OnDestroy {
/**
*
* @param request
* @param assumeUserPresence For WithoutUserInterface requests, we assume the user is present
* @param options For WithoutUserInterface requests, we assume user presence and throw errors if we cannot fulfill the request silently.
* @returns
*/
private convertAssertionRequest(
request:
| autofill.PasskeyAssertionRequest
| autofill.PasskeyAssertionWithoutUserInterfaceRequest,
assumeUserPresence: boolean = false,
options?: {
assumeUserPresence?: boolean,
isSilent?: boolean,
}
): Fido2AuthenticatorGetAssertionParams {
const { assumeUserPresence, isSilent } = options ?? {};
let allowedCredentials;
if ("credentialId" in request) {
allowedCredentials = [
@@ -486,6 +491,7 @@ export class DesktopAutofillService implements OnDestroy {
request.userVerification === "required" || request.userVerification === "preferred",
fallbackSupported: false,
assumeUserPresence,
isSilent,
};
}

View File

@@ -18,6 +18,7 @@ import {
Fido2UserInterfaceSession,
NewCredentialParams,
PickCredentialParams,
UserInteractionRequired,
} from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -130,18 +131,20 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
userVerification,
assumeUserPresence,
masterPasswordRepromptRequired,
isSilent,
}: PickCredentialParams): Promise<{ cipherId: string; userVerified: boolean }> {
this.logService.debug("pickCredential desktop function", {
cipherIds,
userVerification,
assumeUserPresence,
masterPasswordRepromptRequired,
isSilent,
});
try {
// Check if we can return the credential without user interaction
await this.accountService.setShowHeader(false);
if (assumeUserPresence && cipherIds.length === 1 && !masterPasswordRepromptRequired) {
if (cipherIds.length === 1 && !masterPasswordRepromptRequired) {
const selectedCipherId = cipherIds[0];
if (userVerification) {
// retrieve the cipher
@@ -166,7 +169,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
this.logService.debug("Failed to prompt for user verification without showing UI", e)
}
}
else {
else if (assumeUserPresence) {
this.logService.warning(
"shortcut - Assuming user presence and returning cipherId",
cipherIds[0],
@@ -175,11 +178,13 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
}
}
this.logService.debug("Could not shortcut, showing UI");
// TODO: We need to pass context from the original request whether this
// should be a silent request or not. Then, we can fail here if it's
// supposed to be silent.
if (isSilent) {
this.logService.info("Could not fulfill request silently, aborting request");
throw new UserInteractionRequired()
}
else {
this.logService.debug("Could not shortcut, showing UI");
}
// make the cipherIds available to the UI.
this.availableCipherIdsSubject.next(cipherIds);

View File

@@ -153,8 +153,14 @@ export interface Fido2AuthenticatorGetAssertionParams {
/** Forwarded to user interface */
fallbackSupported: boolean;
// Bypass the UI and assume that the user has already interacted with the authenticator
/** Bypass the UI and assume that the user has already interacted with the authenticator */
assumeUserPresence?: boolean;
/** Signals whether an error should be thrown if an assertion cannot be obtained without showing Bitwarden UI.
*
* Note that OS user verification prompts are allowed in silent requests.
*/
isSilent?: boolean;
}
export interface Fido2AuthenticatorGetAssertionResult {

View File

@@ -50,6 +50,12 @@ export interface PickCredentialParams {
* Identifies whether a cipher requires a master password reprompt when getting a credential.
*/
masterPasswordRepromptRequired?: boolean;
/** Signals whether an error should be thrown if an assertion cannot be obtained without showing Bitwarden UI.
*
* Note that OS user verification prompts are allowed in silent requests.
*/
isSilent?: boolean;
}
/**
@@ -121,3 +127,6 @@ export abstract class Fido2UserInterfaceSession {
*/
abstract close(): void;
}
/** Thrown when user interaction is required during a request for a silent assertion. */
export class UserInteractionRequired extends Error {}

View File

@@ -291,6 +291,7 @@ export class Fido2AuthenticatorService<
userVerification: params.requireUserVerification,
assumeUserPresence: params.assumeUserPresence,
masterPasswordRepromptRequired,
isSilent: params.isSilent,
});
}