diff --git a/apps/browser/src/services/fido2/browser-fido2-user-interface.service.ts b/apps/browser/src/services/fido2/browser-fido2-user-interface.service.ts index 6a348fef40a..47bf8aa8b70 100644 --- a/apps/browser/src/services/fido2/browser-fido2-user-interface.service.ts +++ b/apps/browser/src/services/fido2/browser-fido2-user-interface.service.ts @@ -75,6 +75,10 @@ export type BrowserFido2Message = { sessionId: string } & ( type: "ConfirmNewNonDiscoverableCredentialResponse"; cipherId: string; } + | { + type: "InformExcludedCredentialRequest"; + existingCipherIds: string[]; + } | { type: "AbortRequest"; } @@ -222,12 +226,15 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi return response.cipherId; } - informExcludedCredential( - existingCipherIds: string[], - newCredential: NewCredentialParams, - abortController?: AbortController - ): Promise { - return null; + async informExcludedCredential(existingCipherIds: string[]): Promise { + const data: BrowserFido2Message = { + type: "InformExcludedCredentialRequest", + sessionId: this.sessionId, + existingCipherIds, + }; + + await this.send(data); + await this.receive("AbortResponse"); } private async send(msg: BrowserFido2Message): Promise { diff --git a/apps/browser/src/webauthn/popup/fido2/fido2.component.html b/apps/browser/src/webauthn/popup/fido2/fido2.component.html index 07670e71348..53ce8d1b450 100644 --- a/apps/browser/src/webauthn/popup/fido2/fido2.component.html +++ b/apps/browser/src/webauthn/popup/fido2/fido2.component.html @@ -37,6 +37,18 @@ + + A passkey already exists in Bitwarden for this account +
+
+ +
+
+
diff --git a/apps/browser/src/webauthn/popup/fido2/fido2.component.ts b/apps/browser/src/webauthn/popup/fido2/fido2.component.ts index 191002c44a7..665ad613f35 100644 --- a/apps/browser/src/webauthn/popup/fido2/fido2.component.ts +++ b/apps/browser/src/webauthn/popup/fido2/fido2.component.ts @@ -85,6 +85,13 @@ export class Fido2Component implements OnInit, OnDestroy { this.ciphers = (await this.cipherService.getAllDecrypted()).filter( (cipher) => cipher.type === CipherType.Login && !cipher.isDeleted ); + } else if (data?.type === "InformExcludedCredentialRequest") { + this.ciphers = await Promise.all( + data.existingCipherIds.map(async (cipherId) => { + const cipher = await this.cipherService.get(cipherId); + return cipher.decrypt(); + }) + ); } }), takeUntil(this.destroy$) diff --git a/libs/common/src/webauthn/abstractions/fido2-user-interface.service.abstraction.ts b/libs/common/src/webauthn/abstractions/fido2-user-interface.service.abstraction.ts index 8313ec51029..81fe02755e5 100644 --- a/libs/common/src/webauthn/abstractions/fido2-user-interface.service.abstraction.ts +++ b/libs/common/src/webauthn/abstractions/fido2-user-interface.service.abstraction.ts @@ -39,7 +39,6 @@ export abstract class Fido2UserInterfaceSession { ) => Promise; informExcludedCredential: ( existingCipherIds: string[], - newCredential: NewCredentialParams, abortController?: AbortController ) => Promise; } diff --git a/libs/common/src/webauthn/services/fido2-authenticator.service.ts b/libs/common/src/webauthn/services/fido2-authenticator.service.ts index daea9a440e2..055f2b7fa8f 100644 --- a/libs/common/src/webauthn/services/fido2-authenticator.service.ts +++ b/libs/common/src/webauthn/services/fido2-authenticator.service.ts @@ -65,11 +65,8 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr const isExcluded = await this.vaultContainsCredentials(params.excludeCredentialDescriptorList); if (isExcluded) { await userInterfaceSession.informExcludedCredential( - [Utils.guidToStandardFormat(params.excludeCredentialDescriptorList[0].id)], - { - credentialName: params.rpEntity.name, - userName: params.userEntity.displayName, - }, + // [Utils.guidToStandardFormat(params.excludeCredentialDescriptorList[0].id)], + [], abortController );