From 0b7f9aed241fc4fa94bcc6394bf1eeeb1a9f9b88 Mon Sep 17 00:00:00 2001 From: Miles Blackwood Date: Mon, 6 Oct 2025 23:10:02 -0400 Subject: [PATCH] [WIP] Adds preferred algorithm method. --- .../autofill/background/overlay.background.ts | 25 +++++++++++++++++-- .../browser/src/background/main.background.ts | 13 +++++++++- ...redential-generator-service.abstraction.ts | 5 ++++ .../default-credential-generator.service.ts | 17 +++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 61612ce0486..6db50bcd4e0 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { BehaviorSubject, + combineLatest, concatMap, debounceTime, firstValueFrom, @@ -51,7 +52,7 @@ import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-c import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view"; import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; -import { GenerateRequest, Type } from "@bitwarden/generator-core"; +import { CredentialGeneratorService, GenerateRequest, Type } from "@bitwarden/generator-core"; import { GeneratedCredential } from "@bitwarden/generator-history"; // FIXME (PM-22628): Popup imports are forbidden in background @@ -248,6 +249,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { $on: Observable, ) => Observable, private trackCredentialHistory: (password: string) => Promise, + protected generatorService: CredentialGeneratorService, ) { this.initOverlayEventObservables(); } @@ -268,6 +270,9 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.yieldedPassword$ .pipe( concatMap(async (generated) => { + if (!generated) { + return; + } await this.trackCredentialHistory(generated.credential); return generated.credential; }), @@ -3104,7 +3109,23 @@ export class OverlayBackground implements OverlayBackgroundInterface { return false; } - if (!this.credential$.value) { + const autogenerate$ = combineLatest([ + this.credential$, + this.generatorService.preferredAlgorithm$("password", { + account$: this.accountService.activeAccount$, + }), + ]).pipe( + map( + ([ + credential, + { + capabilities: { autogenerate }, + }, + ]) => !credential && autogenerate, + ), + ); + + if (!(await firstValueFrom(autogenerate$))) { this.requestGeneratedPassword({ source: "inline-menu.init", type: Type.password }); } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 4e485e6aeb2..a257812e838 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -223,7 +223,13 @@ import { SearchService } from "@bitwarden/common/vault/services/search.service"; import { TotpService } from "@bitwarden/common/vault/services/totp.service"; import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service"; import { DefaultTaskService, TaskService } from "@bitwarden/common/vault/tasks"; -import { GenerateRequest, Type } from "@bitwarden/generator-core"; +import { + CredentialGeneratorService, + // @TODO + // DefaultCredentialGeneratorService, + GenerateRequest, + Type, +} from "@bitwarden/generator-core"; import { GeneratedCredential } from "@bitwarden/generator-history"; import { legacyPasswordGenerationServiceFactory, @@ -381,6 +387,7 @@ export default class MainBackground { passwordGenerationService: PasswordGenerationServiceAbstraction; syncService: SyncService; passwordStrengthService: PasswordStrengthServiceAbstraction; + credentialGeneratorService: CredentialGeneratorService; totpService: TotpServiceAbstraction; autofillService: AutofillServiceAbstraction; containerService: ContainerService; @@ -1553,6 +1560,9 @@ export default class MainBackground { this.authService, ); + // @TODO + // this.credentialGeneratorService = new DefaultCredentialGeneratorService() + // Synchronous startup if (this.webPushConnectionService instanceof WorkerWebPushConnectionService) { this.webPushConnectionService.start(); @@ -2019,6 +2029,7 @@ export default class MainBackground { this.accountService, this.yieldGeneratedPassword, this.addPasswordToHistory, + this.credentialGeneratorService, ); this.autofillBadgeUpdaterService = new AutofillBadgeUpdaterService( diff --git a/libs/tools/generator/core/src/abstractions/credential-generator-service.abstraction.ts b/libs/tools/generator/core/src/abstractions/credential-generator-service.abstraction.ts index 74f78514594..8c0e62288c3 100644 --- a/libs/tools/generator/core/src/abstractions/credential-generator-service.abstraction.ts +++ b/libs/tools/generator/core/src/abstractions/credential-generator-service.abstraction.ts @@ -41,6 +41,11 @@ export abstract class CredentialGeneratorService { dependencies: BoundDependency<"account", Account>, ) => Observable; + abstract preferredAlgorithm$: ( + type: CredentialType, + dependencies: BoundDependency<"account", Account>, + ) => Observable; + /** Lists metadata for a set of algorithms. * @param type the type or types of algorithms * @returns A list containing the requested metadata. diff --git a/libs/tools/generator/core/src/services/default-credential-generator.service.ts b/libs/tools/generator/core/src/services/default-credential-generator.service.ts index 453139d284c..3dbb36388a0 100644 --- a/libs/tools/generator/core/src/services/default-credential-generator.service.ts +++ b/libs/tools/generator/core/src/services/default-credential-generator.service.ts @@ -1,6 +1,8 @@ import { + Observable, ReplaySubject, concatMap, + distinctUntilChanged, filter, first, map, @@ -32,6 +34,7 @@ import { isForwarderProfile, toVendorId, CredentialType, + AlgorithmMetadata, } from "../metadata"; import { CredentialGeneratorProviders } from "../providers"; import { GenerateRequest } from "../types"; @@ -138,6 +141,20 @@ export class DefaultCredentialGeneratorService implements CredentialGeneratorSer .pipe(map((algorithms) => algorithms.map((a) => this.algorithm(a)))); } + preferredAlgorithm$( + type: CredentialType, + dependencies: BoundDependency<"account", Account>, + ): Observable { + const preference$ = this.provide.metadata.preference$(type, dependencies); + const metadata$ = preference$.pipe( + memoizedMap((preference) => this.provide.metadata.metadata(preference)), + distinctUntilChanged(), + shareReplay({ refCount: true, bufferSize: 1 }), + ); + + return metadata$; + } + algorithms(type: CredentialType | CredentialType[]) { const types: CredentialType[] = Array.isArray(type) ? type : [type]; const algorithms = types