From dbc9b9c90be15c63875be180a4ea3ace88c4a0e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Tue, 16 Jul 2024 19:39:41 +0200 Subject: [PATCH] PM-4877: Only allow replacing passkeys for the same userhandle (#9804) * Initial draft * small cleanup * show vaul items without passkeys * Refactored a bit * tests run for me? * Fixed platform test * null and undefined * lint --- .../browser-fido2-user-interface.service.ts | 3 +++ .../popup/components/fido2/fido2.component.ts | 20 +++++++++++++++++-- ...ido2-user-interface.service.abstraction.ts | 5 +++++ .../fido2/fido2-authenticator.service.spec.ts | 1 + .../fido2/fido2-authenticator.service.ts | 1 + 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/vault/fido2/browser-fido2-user-interface.service.ts b/apps/browser/src/vault/fido2/browser-fido2-user-interface.service.ts index d4ad7209b7..df4f184f7f 100644 --- a/apps/browser/src/vault/fido2/browser-fido2-user-interface.service.ts +++ b/apps/browser/src/vault/fido2/browser-fido2-user-interface.service.ts @@ -65,6 +65,7 @@ export type BrowserFido2Message = { sessionId: string } & ( type: "ConfirmNewCredentialRequest"; credentialName: string; userName: string; + userHandle: string; userVerification: boolean; fallbackSupported: boolean; rpId: string; @@ -242,6 +243,7 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi async confirmNewCredential({ credentialName, userName, + userHandle, userVerification, rpId, }: NewCredentialParams): Promise<{ cipherId: string; userVerified: boolean }> { @@ -250,6 +252,7 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi sessionId: this.sessionId, credentialName, userName, + userHandle, userVerification, fallbackSupported: this.fallbackSupported, rpId, diff --git a/apps/browser/src/vault/popup/components/fido2/fido2.component.ts b/apps/browser/src/vault/popup/components/fido2/fido2.component.ts index 752a910072..3a8c69cba9 100644 --- a/apps/browser/src/vault/popup/components/fido2/fido2.component.ts +++ b/apps/browser/src/vault/popup/components/fido2/fido2.component.ts @@ -143,8 +143,10 @@ export class Fido2Component implements OnInit, OnDestroy { this.ciphers = (await this.cipherService.getAllDecrypted()).filter( (cipher) => cipher.type === CipherType.Login && !cipher.isDeleted, ); - this.displayedCiphers = this.ciphers.filter((cipher) => - cipher.login.matchesUri(this.url, equivalentDomains), + this.displayedCiphers = this.ciphers.filter( + (cipher) => + cipher.login.matchesUri(this.url, equivalentDomains) && + this.hasNoOtherPasskeys(cipher, message.userHandle), ); if (this.displayedCiphers.length > 0) { @@ -405,4 +407,18 @@ export class Fido2Component implements OnInit, OnDestroy { ...msg, }); } + + /** + * This methods returns true if a cipher either has no passkeys, or has a passkey matching with userHandle + * @param userHandle + */ + private hasNoOtherPasskeys(cipher: CipherView, userHandle: string): boolean { + if (cipher.login.fido2Credentials == null || cipher.login.fido2Credentials.length === 0) { + return true; + } + + return cipher.login.fido2Credentials.some((passkey) => { + passkey.userHandle === userHandle; + }); + } } diff --git a/libs/common/src/platform/abstractions/fido2/fido2-user-interface.service.abstraction.ts b/libs/common/src/platform/abstractions/fido2/fido2-user-interface.service.abstraction.ts index aba18f9ecd..9882febdd3 100644 --- a/libs/common/src/platform/abstractions/fido2/fido2-user-interface.service.abstraction.ts +++ b/libs/common/src/platform/abstractions/fido2/fido2-user-interface.service.abstraction.ts @@ -12,6 +12,11 @@ export interface NewCredentialParams { */ userName: string; + /** + * The userhandle (userid) of the user. + */ + userHandle: string; + /** * Whether or not the user must be verified before completing the operation. */ diff --git a/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts b/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts index 5da67f807b..202381c5ea 100644 --- a/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts +++ b/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts @@ -215,6 +215,7 @@ describe("FidoAuthenticatorService", () => { expect(userInterfaceSession.confirmNewCredential).toHaveBeenCalledWith({ credentialName: params.rpEntity.name, userName: params.userEntity.name, + userHandle: Fido2Utils.bufferToString(params.userEntity.id), userVerification, rpId: params.rpEntity.id, } as NewCredentialParams); diff --git a/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts b/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts index 47d76897a3..3464154b9c 100644 --- a/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts +++ b/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts @@ -112,6 +112,7 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr const response = await userInterfaceSession.confirmNewCredential({ credentialName: params.rpEntity.name, userName: params.userEntity.name, + userHandle: Fido2Utils.bufferToString(params.userEntity.id), userVerification: params.requireUserVerification, rpId: params.rpEntity.id, });