1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 13:40:06 +00:00

Add timeout and UV to confirmChoseCipher

UV is performed by UI, not the service
This commit is contained in:
Anders Åberg
2025-03-04 12:40:30 +01:00
parent acdc6b131a
commit 133c68bbab
2 changed files with 33 additions and 16 deletions

View File

@@ -69,7 +69,8 @@ export class Fido2PlaceholderComponent implements OnInit, OnDestroy {
}
async chooseCipher(cipherId: string) {
this.session?.confirmChosenCipher(cipherId);
// For now: Set UV to true
this.session?.confirmChosenCipher(cipherId, true);
await this.router.navigate(["/"]);
await this.desktopSettingsService.setModalMode(false);

View File

@@ -1,5 +1,14 @@
import { Router } from "@angular/router";
import { lastValueFrom, firstValueFrom, map, Subject, filter, take, BehaviorSubject } from "rxjs";
import {
lastValueFrom,
firstValueFrom,
map,
Subject,
filter,
take,
BehaviorSubject,
timeout,
} from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
@@ -96,7 +105,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
take(1),
);
private chosenCipherSubject = new Subject<string>();
private chosenCipherSubject = new Subject<{ cipherId: string; userVerified: boolean }>();
// Method implementation
async pickCredential({
@@ -130,30 +139,37 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
await this.showUi("/passkeys", this.windowObject.windowXy);
const chosenCipherId = await this.waitForUiChosenCipher();
const chosenCipherResponse = await this.waitForUiChosenCipher();
this.logService.debug("Received chosen cipher", chosenCipherId);
if (!chosenCipherId) {
throw new Error("User cancelled");
}
this.logService.debug("Received chosen cipher", chosenCipherResponse);
const resultCipherId = cipherIds.find((id) => id === chosenCipherId);
// TODO: perform userverification
return { cipherId: resultCipherId, userVerified: true };
return {
cipherId: chosenCipherResponse.cipherId,
userVerified: chosenCipherResponse.userVerified,
};
} finally {
// Make sure to clean up so the app is never stuck in modal mode?
await this.desktopSettingsService.setModalMode(false);
}
}
confirmChosenCipher(cipherId: string): void {
this.chosenCipherSubject.next(cipherId);
confirmChosenCipher(cipherId: string, userVerified: boolean = false): void {
this.chosenCipherSubject.next({ cipherId, userVerified });
this.chosenCipherSubject.complete();
}
private async waitForUiChosenCipher(): Promise<string> {
return lastValueFrom(this.chosenCipherSubject);
private async waitForUiChosenCipher(
timeoutMs: number = 60000,
): Promise<{ cipherId: string; userVerified: boolean } | undefined> {
try {
return await lastValueFrom(this.chosenCipherSubject.pipe(timeout(timeoutMs)));
} catch (error) {
// If we hit a timeout, return undefined instead of throwing
this.logService.warning("Timeout: User did not select a cipher within the allowed time", {
timeoutMs,
});
return { cipherId: undefined, userVerified: false };
}
}
/**