From 6179397ba914f62d3a256754763f5f0cd5d81ab5 Mon Sep 17 00:00:00 2001
From: Shane Melton
Date: Wed, 7 Aug 2024 10:59:01 -0700
Subject: [PATCH 01/59] [PM-9503] Add paperclip icon for items with attachments
(#10329)
---
.../vault-list-items-container.component.html | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html
index f6e815dd461..6ac793e4d4d 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html
+++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html
@@ -32,6 +32,11 @@
[size]="'small'"
[appA11yTitle]="orgIconTooltip(cipher)"
>
+
{{ cipher.subTitle }}
From 041cd87e7e7f414c9155f1d8c95804aa841f454d Mon Sep 17 00:00:00 2001
From: Shane Melton
Date: Wed, 7 Aug 2024 12:02:33 -0700
Subject: [PATCH 02/59] [PM-9675] Browser Refresh Login - Generator dialog
(#10352)
* [PM-9675] Introduce CipherFormGenerator component
* [PM-9675] Introduce VaultGeneratorDialog component for Browser
* [PM-9675] Introduce BrowserCipherFormGeneration Service
* [PM-9675] Fix aria label on popup header
* [PM-9675] Cleanup html
* [PM-9675] Cleanup vault generator dialog spec file
---
apps/browser/src/_locales/en/messages.json | 12 +
.../popup/layout/popup-header.component.html | 2 +-
.../add-edit/add-edit-v2.component.ts | 3 +
.../vault-generator-dialog.component.html | 25 +++
.../vault-generator-dialog.component.spec.ts | 82 +++++++
.../vault-generator-dialog.component.ts | 120 ++++++++++
.../browser-cipher-form-generation.service.ts | 45 ++++
.../cipher-form-generator.component.html | 62 ++++++
.../cipher-form-generator.component.spec.ts | 210 ++++++++++++++++++
.../cipher-form-generator.component.ts | 159 +++++++++++++
libs/vault/src/cipher-form/index.ts | 1 +
11 files changed, 720 insertions(+), 1 deletion(-)
create mode 100644 apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.html
create mode 100644 apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.spec.ts
create mode 100644 apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts
create mode 100644 apps/browser/src/vault/popup/services/browser-cipher-form-generation.service.ts
create mode 100644 libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.html
create mode 100644 libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.spec.ts
create mode 100644 libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.ts
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index db1f960b9b3..49d7ae0f3a0 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -1803,6 +1803,18 @@
"passwordGeneratorPolicyInEffect": {
"message": "One or more organization policies are affecting your generator settings."
},
+ "passwordGenerator": {
+ "message": "Password generator"
+ },
+ "usernameGenerator": {
+ "message": "Username generator"
+ },
+ "useThisPassword": {
+ "message": "Use this password"
+ },
+ "useThisUsername": {
+ "message": "Use this username"
+ },
"vaultTimeoutAction": {
"message": "Vault timeout action"
},
diff --git a/apps/browser/src/platform/popup/layout/popup-header.component.html b/apps/browser/src/platform/popup/layout/popup-header.component.html
index 82a2b715a0e..fefc7154314 100644
--- a/apps/browser/src/platform/popup/layout/popup-header.component.html
+++ b/apps/browser/src/platform/popup/layout/popup-header.component.html
@@ -14,7 +14,7 @@
type="button"
*ngIf="showBackButton"
[title]="'back' | i18n"
- [ariaLabel]="'back' | i18n"
+ [attr.aria-label]="'back' | i18n"
[bitAction]="backAction"
>
-
+
{{ "dateCreated" | i18n }}:
{{ cipher.creationDate | date: "medium" }}
@@ -20,7 +26,7 @@
{{ cipher.passwordRevisionDisplayDate | date: "medium" }}
Date: Wed, 7 Aug 2024 17:09:53 -0400
Subject: [PATCH 06/59] PM-10486 hide empty cardholder field (#10435)
---
.../cipher-view/card-details/card-details-view.component.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/vault/src/cipher-view/card-details/card-details-view.component.html b/libs/vault/src/cipher-view/card-details/card-details-view.component.html
index b83200cadae..c446ba4f319 100644
--- a/libs/vault/src/cipher-view/card-details/card-details-view.component.html
+++ b/libs/vault/src/cipher-view/card-details/card-details-view.component.html
@@ -3,7 +3,7 @@
{{ setSectionTitle }}
-
+
{{ "cardholderName" | i18n }}
Date: Wed, 7 Aug 2024 16:25:19 -0500
Subject: [PATCH 07/59] [PM-6471] Implement Inline Menu Autofill for Passkeys
(#10127)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* PM-4661: Add passkey.username as item.username (#9756)
* Add incoming passkey.username as item.username
* Driveby fix, was sending wrong username
* added username to new-cipher too
* Guarded the if-block
* Update apps/browser/src/vault/popup/components/vault/add-edit.component.ts
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
* Fixed broken test
* fixed username on existing ciphers
---------
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
* PM-4878: Add passkey information to items when signing in (#9835)
* Added username to subtitle
* Added subName to cipher
* Moved subName to component
* Update apps/browser/src/vault/popup/components/fido2/fido2-cipher-row.component.ts
Co-authored-by: SmithThe4th
* Fixed double code and added comment
* Added changeDetection: ChangeDetectionStrategy.OnPush as per review
---------
Co-authored-by: SmithThe4th
* [AC-2791] Members page - finish component library refactors (#9727)
* Replace PlatformUtilsService with ToastService
* Remove unneeded templates
* Implement table filtering function
* Move member-only methods from base class to subclass
* Move utility functions inside new MemberTableDataSource
* Rename PeopleComponent to MembersComponent
* [deps] Platform: Update angular-cli monorepo to v16.2.14 (#9380)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* [PM-8789] Move desktop_native into subcrate (#9682)
* Move desktop_native into subcrate
* Add publish = false to crates
* [PM-6394] remove policy evaluator cache (#9807)
* [PM-9364] Copy for Aggregate auto-scaling invoices for Teams and Enterprise customers (#9875)
* Change the seat adjustment message
* Move changes from en_GB file to en file
* revert changes in en_GB file
* Add feature flag to the change
* use user verification as a part of key rotation (#9722)
* Add the ability for custom validation logic to be injected into `UserVerificationDialogComponent` (#8770)
* Introduce `verificationType`
* Update template to use `verificationType`
* Implement a path for `verificationType = 'custom'`
* Delete `clientSideOnlyVerification`
* Update `EnrollMasterPasswordResetComponent` to include a server-side hash check
* Better describe the custom scenerio through comments
* Add an example of the custom verficiation scenerio
* Move execution of verification function into try/catch
* Migrate existing uses of `clientSideOnlyVerification`
* Use generic type option instead of casting
* Change "given" to "determined" in a comment
* Restructure the `org-redirect` guard to be Angular 17+ compliant (#9552)
* Document the `org-redirect` guard in code
* Make assertions about the way the `org-redirect` guard should behave
* Restructure the `org-redirect` guard to be Angular 17+ compliant
* Convert data parameter to function parameter
* Convert a data parameter to a function parameter that was missed
* Pass redirect function to default organization route
* don't initialize kdf with validators, do it on first set (#9754)
* add testids for attachments (#9892)
* Bug fix - error toast in 2fa (#9623)
* Bug fix - error toast in 2fa
* Bug fix - Yubikey code obscured
* 2FA error fix
* [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account
* [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account
* [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account
* [PM-2858] Fixing icon color
* [PM-2858] Adding subtitle for identity inline menu list items
* [PM-2858] Fixing jest tests
* [PM-2858] Working through implementation of conditional identity fill logic on inline menu
* [PM-2858] Working through implementation of conditional identity fill logic on inline menu
* [PM-2858] Working through implementation of conditional identity fill logic on inline menu
* [PM-2858] Working through implementation of conditional identity fill logic on inline menu
* [PM-2858] Working through implementation of conditional identity fill logic on inline menu
* [PM-2858] Working through implementation of conditional identity fill logic on inline menu
* [PM-2858] Working through implementation of conditional identity fill logic on inline menu
* [PM-2858] Working through implementation of conditional identity fill logic on inline menu
* [PM-2858] Working through identity field qualification for the inline menu
* [PM-2858] Working through identity field qualification for the inline menu
* [PM-2858] Working through identity field qualification for the inline menu
* [PM-2858] Working through identity field qualification for the inline menu
* [PM-2858] Working through identity field qualification for the inline menu
* [PM-2858] Working through identity field qualification for the inline menu
* [PM-2858] Scaffolding add new identity logic
* [PM-2858] Implementing add new identity
* [PM-2858] Implementing add new identity
* [PM-2858] Scaffolding add new identity logic
* [PM-2858] Scaffolding add new identity logic
* [PM-2858] Scaffolding add new identity logic
* [PM-2857] Fixing an issue with how we parse the last digits for credit card aria description
* [PM-2857] Setting up logic to ensrue we use a set email address as a fallback for a username
* [PM-2857] Fixing an issue with how we parse the last digits for credit card aria description
* [PM-2858] Reverting forced email address in inline menu identity autofill
* Restructure the `is-paid-org` guard to be Angular 17+ compliant (#9598)
* Document that `is-paid-org` guard in code
* Remove unused `MessagingService` dependency
* Make assertions about the way the is-paid-org guard should behave
* Restructure the `is-paid-org` guard to be Angular 17+ compliant
* Random commit to get the build job moving
* Undo previous commit
* Bumped client version(s) (#9895)
* [PM-9344] Clarify accepted user state (#9861)
* Prefer `Needs confirmation` to `Accepted` display status
This emphasizes that action is still required to complete setup.
* Remove unused message
* Bumped client version(s) (#9906)
* Revert "Bumped client version(s) (#9906)" (#9907)
This reverts commit 78c28297938eda53e7731fdf9f63d7baa7068d0d.
* fix duo subscriptions and org vs individual duo setup (#9859)
* [PM-5024] Migrate tax-info component (#9872)
* Changes for the tax info migration
* Return for invalid formgroup
* Restructure the `org-permissions` guard to be Angular 17+ compliant (#9631)
* Document the `org-permissions` guard in code
* Restructure the `org-permissions` guard to be Angular 17+ compliant
* Update the `org-permissions` guard to use `ToastService`
* Simplify callback function sigantures
* Remove unused test object
* Fix updated route from merge
* Restructure the `provider-permissions` guard to be Angular 17+ compliant (#9609)
* Document the `provider-permissions` guard in code
* Restructure the `provider-permissions` guard to be Angular 17+ compliant
* [deps] Platform: Update @types/argon2-browser to v1.18.4 (#8180)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* Bumped client version(s) (#9914)
* [PM-7162] Cipher Form - Item Details (#9758)
* [PM-7162] Fix weird angular error regarding disabled component bit-select
* [PM-7162] Introduce CipherFormConfigService and related types
* [PM-7162] Introduce CipherFormService
* [PM-7162] Introduce the Item Details section component and the CipherFormContainer interface
* [PM-7162] Introduce the CipherForm component
* [PM-7162] Add strongly typed QueryParams to the add-edit-v2.component
* [PM-7162] Export CipherForm from Vault Lib
* [PM-7162] Use the CipherForm in Browser AddEditV2
* [PM-7162] Introduce CipherForm storybook
* [PM-7162] Remove VaultPopupListFilterService dependency from NewItemDropDownV2 component
* [PM-7162] Add support for content projection of attachment button
* [PM-7162] Fix typo
* [PM-7162] Cipher form service cleanup
* [PM-7162] Move readonly collection notice to bit-hint
* [PM-7162] Refactor CipherFormConfig type to enforce required properties with Typescript
* [PM-7162] Fix storybook after config changes
* [PM-7162] Use new add-edit component for clone route
* [deps]: Update @yao-pkg/pkg to ^5.12.0 (#9820)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* Autosync the updated translations (#9922)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* Autosync the updated translations (#9923)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* Autosync the updated translations (#9924)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* [AC-2830] Unable to create a free organization (#9917)
* Resolve the issue free org creation
* Check that the taxForm is touched
* [PM-7162] Fix broken getter when original cipher is null (#9927)
* [PM-8525] Edit Card (#9901)
* initial add of card details section
* add card number
* update card brand when the card number changes
* add year and month fields
* add security code field
* hide number and security code by default
* add `id` for all form fields
* update select options to match existing options
* make year input numerical
* only display card details for card ciphers
* use style to set input height
* handle numerical values for year
* update heading when a brand is available
* remove unused ref
* use cardview types for the form
* fix numerical input type
* disable card details when in partial-edit mode
* remove hardcoded height
* update types for formBuilder
* [PM-9440] Fix: handle undefined value in migration 66 (#9908)
* fix: handle undefined value in migration 66
* fix: the if-statement was typo
* Rename "encryptionAlgorithm" to "hashAlgorithmForEncryption" for clarity (#9891)
* [PM-7972] Account switching integration with "remember email" functionality (#9750)
* add account switching logic to login email service
* enforce boolean and fix desktop account switcher order
* [PM-9442] Add tests for undefined state values and proper emulation of ElectronStorageService in tests (#9910)
* fix: handle undefined value in migration 66
* fix: the if-statement was typo
* feat: duplicate error behavior in fake storage service
* feat: fix all migrations that were setting undefined values
* feat: add test for disabled fingrint in migration 66
* fix: default single user state saving undefined value to state
* revert: awaiting floating promise
gonna fix this in a separate PR
* Revert "feat: fix all migrations that were setting undefined values"
This reverts commit 034713256cee9a8e164295c88157fe33d8372c81.
* feat: automatically convert save to remove
* Revert "fix: default single user state saving undefined value to state"
This reverts commit 6c36da6ba52f6886d0de2b502b3aaff7f122c3a7.
* [AC-2805] Consolidated Billing UI Updates (#9893)
* Add empty state for invoices
* Make cards on create client dialog tabbable
* Add space in $ / month per member
* Mute text, remove (Monthly) and right align menu on clients table
* Made used seats account for all users and fixed column sort for used/remaining
* Resize pricing cards
* Rename assignedSeats to occupiedSeats
* [PM-9460][deps] Tools: Update electron to v31 (#9921)
* [deps] Tools: Update electron to v31
* Bump version in electron-builder
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel James Smith
* [AC-1452] Restrict access to 'Organization Info' and 'Two-Step Login' settings pages with a permission check (#9483)
* Guard Organization Info route - Owners only
* Guard TwoFactor route - Owners only and Organization must be able to use 2FA
* Update guards to use function syntax
---------
Co-authored-by: Addison Beck
* [PM-9437] Use CollectionAccessDetailsResponse type now that is always the type returned from the API (#9951)
* Add required env variables to desktop native build script (#9869)
* [AC-2676] Remove paging logic from GroupsComponent (#9705)
* remove infinite scroll, use virtual scroll instead
* use TableDataSource for search
* allow sorting by name
* replacing PlatformUtilsService.showToast with ToastService
* misc FIXMEs
* [PM-9441] Catch and log exceptions during migration (#9905)
* feat: catch and log exceptions during migration
* Revert "feat: catch and log exceptions during migration"
This reverts commit d68733b7e58120298974b350e496bb3e0c9af0d2.
* feat: use log service to log migration errors
* Autosync the updated translations (#9972)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* Autosync the updated translations (#9973)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* Updated codeowners for new design system team (#9913)
* Updated codeowners for new design system team.
* Moved Angular and Bootstrap dependencies
* Moved additional dependencies.
* Updated ownership
Co-authored-by: Will Martin
---------
Co-authored-by: Will Martin
* [SM-1016] Fix new access token dialog (#9918)
* swap to bit-dialog title & subtitle
* remove dialogRef.disableClose & use toastService
* Add shared two-factor-options component (#9767)
* Communicate the upcoming client vault privacy changes to MSPs (#9994)
* Add a banner notification to the provider portal
* Feature flag the banner
* Move banner copy to messages.json
* Allow for dismissing the banner
* Auth/PM-7321 - Registration with Email Verification - Registration Finish Component Implementation (#9653)
* PM-7321 - Temp add input password
* PM-7321 - update input password based on latest PR changes to test.
* PM-7321 - Progress on testing input password component + RegistrationFinishComponent checks
* PM-7321 - more progress on registration finish.
* PM-7321 - Wire up RegistrationFinishRequest model + AccountApiService abstraction + implementation changes for new method.
* PM-7321 - WIP Registration Finish - wiring up request building and API call on submit.
* PM-7321 - WIP registratin finish
* PM-7321 - WIP on creating registration-finish service + web override to add org invite handling
* PM-7321 - (1) Move web-registration-finish svc to web (2) Wire up exports (3) wire up RegistrationFinishComponent to call registration finish service
* PM-7321 - Get CLI building
* PM-7321 - Move all finish registration service and content to registration-finish feature folder.
* PM-7321 - Fix RegistrationFinishService config
* PM-7321 - RegistrationFinishComponent- handlePasswordFormSubmit - error handling WIP
* PM-7321 - InputPasswordComp - Update to accept masterPasswordPolicyOptions as input instead of retrieving it as parent components in different scenarios will need to retrieve the policies differently (e.g., orgInvite token in registration vs direct call via org id post SSO on set password)
* PM-7321 - Registration Finish - Add web specific logic for retrieving master password policies and passing them into the input password component.
* PM-7321 - Registration Start - Send email via query param to registration finish page so it can create masterKey
* PM-7321 - InputPassword comp - (1) Add loading input (2) Add email validation to submit logic.
* PM-7321 - Registration Finish - Add submitting state and pass into input password so that the rest of the registration process keeps the child form disabled.
* PM-7321 - Registration Finish - use validation service for error handling.
* PM-7321 - All register routes must be dynamic and change if the feature flag changes.
* PM-7321 - Test registration finish services.
* PM-7321 - RegisterRouteService - Add comment documenting why the service exists.
* PM-7321 - Add missing input password translations to browser & desktop
* PM-7321 - WebRegistrationFinishSvc - apply PR feedback
* [deps] Autofill: Update rimraf to v5.0.8 (#10008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* [PM-9318] Fix username on protonpass import (#9889)
* Fix username field used for ProtonPass import
ProtonPass has changed their export format and userName is not itemEmail
* Import additional field itemUsername
---------
Co-authored-by: Daniel James Smith
* [PM-8943] Update QRious script initialization in Authenticator two-factor provider (#9926)
* create onload() for qrious as well as error messaging if QR code cannot be displayed
* button and message updates and formpromise removal
* load QR script async
* rename and reorder methods
* Delete Unused Bits of StateService (#9858)
* Delete Unused Bits of StateService
* Fix Tests
* remove getBgService for auth request service (#10020)
* [PM-2858] Fixing an issue found when the first or last names of an identity are not filled
* [PM-2858] Fixing an issue found where keyboard navigation can potentially close the inline menu
* [PM-2858] Fixing jest tests within inline menu list
* [PM-2858] Fixing jest tests within inline menu list
* [PM-2858] Setting up login items to be presented when an account creation form is shown to the user
* [PM-2858] Refactoring implementation used for creating the inline menu cipher data
* [PM-2858] Refactoring implementation used for creating the inline menu cipher data
* [PM-2858] Refactoring implementation used for creating the inline menu cipher data
* [PM-2858] Refactoring implementation
* [PM-2858] Refactoring implementation
* [PM-2858] Refactoring implementation
* [PM-2858] Refactoring implementation
* [PM-2858] Changing how we populate login ciphers within create account
* [PM-2858] Adding documentation
* [PM-2858] Working through jest tests for the OverlayBackground
* [PM-2858] Working through jest tests for the OverlayBackground
* [PM-2858] Working through jest tests for the AutofillInlineMenuList class
* [PM-2858] Adding documentation to inline menu list methods
* [PM-2857] Fixing a jest test
* [PM-2858] Fixing jest tests within inline menu list
* [PM-2858] Addressing jest tests within AutofillOverlayContentService
* [PM-2858] Addressing jest tests within AutofillOverlayContentService
* [PM-2858] Addressing jest tests within InlineMenuFieldQualificationService
* [PM-9267] Implement feature flag for inline menu re-architecture (#9845)
* [PM-9267] Implement Feature Flag for Inline Menu Re-Architecture
* [PM-9267] Incorporating legacy OverlayBackground implementation
* [PM-9267] Incorporating legacy overlay content scripts
* [PM-9267] Incorporating legacy overlay content scripts
* [PM-9267] Incorporating legacy overlay content scripts
* [PM-9267] Incorporating legacy overlay content scripts
* [PM-9267] Finalizing feature flag implementation
* [PM-9267] Finalizing feature flag implementation
* [PM-9267] Finalizing feature flag implementation
* [PM-9267] Finalizing feature flag implementation
* [PM-9267] Finalizing feature flag implementation
* [PM-9267] Finalizing feature flag implementation
* [PM-9267] Finalizing feature flag implementation
* [PM-9267] Finalizing feature flag implementation
* [PM-9267] Adjusting naming convention for page files
* [PM-9267] Adjusting naming convention for page files
* [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account
* PM-4950 - Fix hint and verify delete components that had the data in the wrong place (#9877)
* PM-4661: Add passkey.username as item.username (#9756)
* Add incoming passkey.username as item.username
* Driveby fix, was sending wrong username
* added username to new-cipher too
* Guarded the if-block
* Update apps/browser/src/vault/popup/components/vault/add-edit.component.ts
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
* Fixed broken test
* fixed username on existing ciphers
---------
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
* PM-4878: Add passkey information to items when signing in (#9835)
* Added username to subtitle
* Added subName to cipher
* Moved subName to component
* Update apps/browser/src/vault/popup/components/fido2/fido2-cipher-row.component.ts
Co-authored-by: SmithThe4th
* Fixed double code and added comment
* Added changeDetection: ChangeDetectionStrategy.OnPush as per review
---------
Co-authored-by: SmithThe4th
* [AC-2791] Members page - finish component library refactors (#9727)
* Replace PlatformUtilsService with ToastService
* Remove unneeded templates
* Implement table filtering function
* Move member-only methods from base class to subclass
* Move utility functions inside new MemberTableDataSource
* Rename PeopleComponent to MembersComponent
* [deps] Platform: Update angular-cli monorepo to v16.2.14 (#9380)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* [PM-8789] Move desktop_native into subcrate (#9682)
* Move desktop_native into subcrate
* Add publish = false to crates
* [PM-6394] remove policy evaluator cache (#9807)
* [PM-9364] Copy for Aggregate auto-scaling invoices for Teams and Enterprise customers (#9875)
* Change the seat adjustment message
* Move changes from en_GB file to en file
* revert changes in en_GB file
* Add feature flag to the change
* use user verification as a part of key rotation (#9722)
* Add the ability for custom validation logic to be injected into `UserVerificationDialogComponent` (#8770)
* Introduce `verificationType`
* Update template to use `verificationType`
* Implement a path for `verificationType = 'custom'`
* Delete `clientSideOnlyVerification`
* Update `EnrollMasterPasswordResetComponent` to include a server-side hash check
* Better describe the custom scenerio through comments
* Add an example of the custom verficiation scenerio
* Move execution of verification function into try/catch
* Migrate existing uses of `clientSideOnlyVerification`
* Use generic type option instead of casting
* Change "given" to "determined" in a comment
* Restructure the `org-redirect` guard to be Angular 17+ compliant (#9552)
* Document the `org-redirect` guard in code
* Make assertions about the way the `org-redirect` guard should behave
* Restructure the `org-redirect` guard to be Angular 17+ compliant
* Convert data parameter to function parameter
* Convert a data parameter to a function parameter that was missed
* Pass redirect function to default organization route
* don't initialize kdf with validators, do it on first set (#9754)
* add testids for attachments (#9892)
* Bug fix - error toast in 2fa (#9623)
* Bug fix - error toast in 2fa
* Bug fix - Yubikey code obscured
* 2FA error fix
* Restructure the `is-paid-org` guard to be Angular 17+ compliant (#9598)
* Document that `is-paid-org` guard in code
* Remove unused `MessagingService` dependency
* Make assertions about the way the is-paid-org guard should behave
* Restructure the `is-paid-org` guard to be Angular 17+ compliant
* Random commit to get the build job moving
* Undo previous commit
* Bumped client version(s) (#9895)
* [PM-9344] Clarify accepted user state (#9861)
* Prefer `Needs confirmation` to `Accepted` display status
This emphasizes that action is still required to complete setup.
* Remove unused message
* Bumped client version(s) (#9906)
* Revert "Bumped client version(s) (#9906)" (#9907)
This reverts commit 78c28297938eda53e7731fdf9f63d7baa7068d0d.
* fix duo subscriptions and org vs individual duo setup (#9859)
* [PM-5024] Migrate tax-info component (#9872)
* Changes for the tax info migration
* Return for invalid formgroup
* Restructure the `org-permissions` guard to be Angular 17+ compliant (#9631)
* Document the `org-permissions` guard in code
* Restructure the `org-permissions` guard to be Angular 17+ compliant
* Update the `org-permissions` guard to use `ToastService`
* Simplify callback function sigantures
* Remove unused test object
* Fix updated route from merge
* Restructure the `provider-permissions` guard to be Angular 17+ compliant (#9609)
* Document the `provider-permissions` guard in code
* Restructure the `provider-permissions` guard to be Angular 17+ compliant
* [deps] Platform: Update @types/argon2-browser to v1.18.4 (#8180)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* Bumped client version(s) (#9914)
* [PM-7162] Cipher Form - Item Details (#9758)
* [PM-7162] Fix weird angular error regarding disabled component bit-select
* [PM-7162] Introduce CipherFormConfigService and related types
* [PM-7162] Introduce CipherFormService
* [PM-7162] Introduce the Item Details section component and the CipherFormContainer interface
* [PM-7162] Introduce the CipherForm component
* [PM-7162] Add strongly typed QueryParams to the add-edit-v2.component
* [PM-7162] Export CipherForm from Vault Lib
* [PM-7162] Use the CipherForm in Browser AddEditV2
* [PM-7162] Introduce CipherForm storybook
* [PM-7162] Remove VaultPopupListFilterService dependency from NewItemDropDownV2 component
* [PM-7162] Add support for content projection of attachment button
* [PM-7162] Fix typo
* [PM-7162] Cipher form service cleanup
* [PM-7162] Move readonly collection notice to bit-hint
* [PM-7162] Refactor CipherFormConfig type to enforce required properties with Typescript
* [PM-7162] Fix storybook after config changes
* [PM-7162] Use new add-edit component for clone route
* [deps]: Update @yao-pkg/pkg to ^5.12.0 (#9820)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* Autosync the updated translations (#9922)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* Autosync the updated translations (#9923)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* Autosync the updated translations (#9924)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* [AC-2830] Unable to create a free organization (#9917)
* Resolve the issue free org creation
* Check that the taxForm is touched
* [PM-7162] Fix broken getter when original cipher is null (#9927)
* [PM-8525] Edit Card (#9901)
* initial add of card details section
* add card number
* update card brand when the card number changes
* add year and month fields
* add security code field
* hide number and security code by default
* add `id` for all form fields
* update select options to match existing options
* make year input numerical
* only display card details for card ciphers
* use style to set input height
* handle numerical values for year
* update heading when a brand is available
* remove unused ref
* use cardview types for the form
* fix numerical input type
* disable card details when in partial-edit mode
* remove hardcoded height
* update types for formBuilder
* [PM-9440] Fix: handle undefined value in migration 66 (#9908)
* fix: handle undefined value in migration 66
* fix: the if-statement was typo
* Rename "encryptionAlgorithm" to "hashAlgorithmForEncryption" for clarity (#9891)
* [PM-7972] Account switching integration with "remember email" functionality (#9750)
* add account switching logic to login email service
* enforce boolean and fix desktop account switcher order
* [PM-9442] Add tests for undefined state values and proper emulation of ElectronStorageService in tests (#9910)
* fix: handle undefined value in migration 66
* fix: the if-statement was typo
* feat: duplicate error behavior in fake storage service
* feat: fix all migrations that were setting undefined values
* feat: add test for disabled fingrint in migration 66
* fix: default single user state saving undefined value to state
* revert: awaiting floating promise
gonna fix this in a separate PR
* Revert "feat: fix all migrations that were setting undefined values"
This reverts commit 034713256cee9a8e164295c88157fe33d8372c81.
* feat: automatically convert save to remove
* Revert "fix: default single user state saving undefined value to state"
This reverts commit 6c36da6ba52f6886d0de2b502b3aaff7f122c3a7.
* [AC-2805] Consolidated Billing UI Updates (#9893)
* Add empty state for invoices
* Make cards on create client dialog tabbable
* Add space in $ / month per member
* Mute text, remove (Monthly) and right align menu on clients table
* Made used seats account for all users and fixed column sort for used/remaining
* Resize pricing cards
* Rename assignedSeats to occupiedSeats
* [PM-9460][deps] Tools: Update electron to v31 (#9921)
* [deps] Tools: Update electron to v31
* Bump version in electron-builder
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel James Smith
* [AC-1452] Restrict access to 'Organization Info' and 'Two-Step Login' settings pages with a permission check (#9483)
* Guard Organization Info route - Owners only
* Guard TwoFactor route - Owners only and Organization must be able to use 2FA
* Update guards to use function syntax
---------
Co-authored-by: Addison Beck
* [PM-9437] Use CollectionAccessDetailsResponse type now that is always the type returned from the API (#9951)
* Add required env variables to desktop native build script (#9869)
* [AC-2676] Remove paging logic from GroupsComponent (#9705)
* remove infinite scroll, use virtual scroll instead
* use TableDataSource for search
* allow sorting by name
* replacing PlatformUtilsService.showToast with ToastService
* misc FIXMEs
* [PM-9441] Catch and log exceptions during migration (#9905)
* feat: catch and log exceptions during migration
* Revert "feat: catch and log exceptions during migration"
This reverts commit d68733b7e58120298974b350e496bb3e0c9af0d2.
* feat: use log service to log migration errors
* Autosync the updated translations (#9972)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* Autosync the updated translations (#9973)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
* Updated codeowners for new design system team (#9913)
* Updated codeowners for new design system team.
* Moved Angular and Bootstrap dependencies
* Moved additional dependencies.
* Updated ownership
Co-authored-by: Will Martin
---------
Co-authored-by: Will Martin
* [SM-1016] Fix new access token dialog (#9918)
* swap to bit-dialog title & subtitle
* remove dialogRef.disableClose & use toastService
* Add shared two-factor-options component (#9767)
* Communicate the upcoming client vault privacy changes to MSPs (#9994)
* Add a banner notification to the provider portal
* Feature flag the banner
* Move banner copy to messages.json
* Allow for dismissing the banner
* Auth/PM-7321 - Registration with Email Verification - Registration Finish Component Implementation (#9653)
* PM-7321 - Temp add input password
* PM-7321 - update input password based on latest PR changes to test.
* PM-7321 - Progress on testing input password component + RegistrationFinishComponent checks
* PM-7321 - more progress on registration finish.
* PM-7321 - Wire up RegistrationFinishRequest model + AccountApiService abstraction + implementation changes for new method.
* PM-7321 - WIP Registration Finish - wiring up request building and API call on submit.
* PM-7321 - WIP registratin finish
* PM-7321 - WIP on creating registration-finish service + web override to add org invite handling
* PM-7321 - (1) Move web-registration-finish svc to web (2) Wire up exports (3) wire up RegistrationFinishComponent to call registration finish service
* PM-7321 - Get CLI building
* PM-7321 - Move all finish registration service and content to registration-finish feature folder.
* PM-7321 - Fix RegistrationFinishService config
* PM-7321 - RegistrationFinishComponent- handlePasswordFormSubmit - error handling WIP
* PM-7321 - InputPasswordComp - Update to accept masterPasswordPolicyOptions as input instead of retrieving it as parent components in different scenarios will need to retrieve the policies differently (e.g., orgInvite token in registration vs direct call via org id post SSO on set password)
* PM-7321 - Registration Finish - Add web specific logic for retrieving master password policies and passing them into the input password component.
* PM-7321 - Registration Start - Send email via query param to registration finish page so it can create masterKey
* PM-7321 - InputPassword comp - (1) Add loading input (2) Add email validation to submit logic.
* PM-7321 - Registration Finish - Add submitting state and pass into input password so that the rest of the registration process keeps the child form disabled.
* PM-7321 - Registration Finish - use validation service for error handling.
* PM-7321 - All register routes must be dynamic and change if the feature flag changes.
* PM-7321 - Test registration finish services.
* PM-7321 - RegisterRouteService - Add comment documenting why the service exists.
* PM-7321 - Add missing input password translations to browser & desktop
* PM-7321 - WebRegistrationFinishSvc - apply PR feedback
* [deps] Autofill: Update rimraf to v5.0.8 (#10008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* [PM-9318] Fix username on protonpass import (#9889)
* Fix username field used for ProtonPass import
ProtonPass has changed their export format and userName is not itemEmail
* Import additional field itemUsername
---------
Co-authored-by: Daniel James Smith
* [PM-8943] Update QRious script initialization in Authenticator two-factor provider (#9926)
* create onload() for qrious as well as error messaging if QR code cannot be displayed
* button and message updates and formpromise removal
* load QR script async
* rename and reorder methods
* Delete Unused Bits of StateService (#9858)
* Delete Unused Bits of StateService
* Fix Tests
* remove getBgService for auth request service (#10020)
---------
Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
Co-authored-by: Anders Åberg
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
Co-authored-by: SmithThe4th
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel García
Co-authored-by: ✨ Audrey ✨
Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com>
Co-authored-by: Jake Fink
Co-authored-by: Addison Beck
Co-authored-by: Nick Krantz <125900171+nick-livefront@users.noreply.github.com>
Co-authored-by: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com>
Co-authored-by: Bitwarden DevOps <106330231+bitwarden-devops-bot@users.noreply.github.com>
Co-authored-by: Matt Gibson
Co-authored-by: Opeyemi
Co-authored-by: Shane Melton
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Andreas Coroiu
Co-authored-by: Bernd Schoolmann
Co-authored-by: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com>
Co-authored-by: Daniel James Smith
Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com>
Co-authored-by: Addison Beck
Co-authored-by: Todd Martin <106564991+trmartin4@users.noreply.github.com>
Co-authored-by: Will Martin
Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>
Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com>
Co-authored-by: Ike <137194738+ike-kottlowski@users.noreply.github.com>
* [PM-1223] Passkeys Mediated Conditional UI
* [PM-1223] Passkeys Mediated Conditional UI
* [PM-1223] Finished migrating existing POC solution
* [PM-1223] Setting up passkeys to appear before login ciphers
* [PM-6471] Implement on-page autofill menu for passkeys
* [PM-6471] Working through visual presentation of passkeys within inline menu
* [PM-6471] Implementing visual and behavior differences between inline menu passkeys and regular login elements
* [PM-6471] Adding a11y content within inline menu list elements
* [PM-6471] Fixing issue with SVG path fill on new passkey icon
* [PM-6471] Working through scroll event triggers
* [PM-6471] Refactoring onScroll implementation
* [PM-6471] Adding a methodology for allow users to cancel a conditional UI workflow, but still be able to re-trigger the passkey fill
* [PM-2858] Fixing an issue found where password fields addedin new account forms do not properly pull their value into the add cipher flow
* [PM-6471] Implementing a methodology for exlusively displaying credentials that are authorized within the fido2 request
* [PM-6471] Adding the webAuthn autocomplete value to the field qualification service
* [PM-6471] Fixing issues within OverlayBackground jest tests
* [PM-6471] Fixing issues within AutofillInlineMenuList jest tests
* [PM-6471] Adding jest tests for the OverlayBackground
* [PM-6471] Adding jest tests for the OverlayBackground
* [PM-6471] Adding jest tests for the OverlayBackground
* [PM-6471] Re-adding an optimization to the inline menu list
* [PM-6471] Refactoring implementation, optimizing scroll behavior within the inline menu, and adding a method for ensureing passkeys get set as the most recently used cipher when fill occurs
* [PM-6471] Refactoring implementation, optimizing scroll behavior within the inline menu, and adding a method for ensureing passkeys get set as the most recently used cipher when fill occurs
* [PM-6471] Refactoring how we identify a cipher as a passkey cipher
* [PM-6471] Reworking implementation to loop mediated conditional request until a valid value is returned rather than re-calling navigator API
* [PM-6471] Adding jest tests for the inline menu list logic
* [PM-6471] Adding jest tests for the inline menu list logic
* [PM-6471] Adding jest tests for conditional mediated webauthn request
* [PM-6471] Removing unnecessary comment
* [PM-6471] Adding jest tests for incorporated Fido2ClientService changes
* [PM-6471] Adding jest tests to the Fido2AuthenticatorService changes
* [PM-6471] Adding jest tests for the Fido2ActiveRequestManager class
* [PM-6471] Fixing issue with master password reprompt not triggering for cipher when user verification is discouraged
* [PM-2858] Adjusting scrollbar stylings
* [PM-2858] Adjusting how we handle instantiating the feature flag guarded overlay background and how we handle instantiating identities and card ciphers in the inline menu
* [PM-2858] Adjusting how we handle instantiating the feature flag guarded overlay background and how we handle instantiating identities and card ciphers in the inline menu
* [PM-2858] Adjusting how we handle instantiating the feature flag guarded overlay background and how we handle instantiating identities and card ciphers in the inline menu
* [PM-2858] Incorporating some changes that ensure the inline menu list fades in as expected
* [PM-2858] Incorporating some changes that ensure the inline menu list fades in as expected
* [PM-2858] Incorporating some changes that ensure the inline menu list fades in as expected
* [PM-2858] Adjusting how we inject translations for a couple of aria label elements
* [PM-6471] Merging changes from identities branch
* [PM-6471] Fixing an issue relating to a current tab reference
* [PM-6471] Fixing an issue relating to a current tab reference
* [PM-6471] Optimizing conditional logic for OverlayBackground.showCipherAsPasskey
* [PM-6471] Refactoring implementation
* [PM-6471] Refactoring implementation
* [PM-6471] Adding coverage for cases where a mediated conditional request is aborted
* [PM-6471] Fixing typechecking error
---------
Co-authored-by: Anders Åberg
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
Co-authored-by: SmithThe4th
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel García
Co-authored-by: ✨ Audrey ✨
Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com>
Co-authored-by: Jake Fink
Co-authored-by: Addison Beck
Co-authored-by: Nick Krantz <125900171+nick-livefront@users.noreply.github.com>
Co-authored-by: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com>
Co-authored-by: Bitwarden DevOps <106330231+bitwarden-devops-bot@users.noreply.github.com>
Co-authored-by: Matt Gibson
Co-authored-by: Opeyemi
Co-authored-by: Shane Melton
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Andreas Coroiu
Co-authored-by: Bernd Schoolmann
Co-authored-by: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com>
Co-authored-by: Daniel James Smith
Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com>
Co-authored-by: Addison Beck
Co-authored-by: Todd Martin <106564991+trmartin4@users.noreply.github.com>
Co-authored-by: Will Martin
Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>
Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com>
Co-authored-by: Ike <137194738+ike-kottlowski@users.noreply.github.com>
---
apps/browser/src/_locales/en/messages.json | 12 +
.../abstractions/overlay.background.ts | 19 +-
.../background/overlay.background.spec.ts | 238 +++++++--
.../autofill/background/overlay.background.ts | 158 +++++-
.../fido2/content/fido2-page-script.ts | 44 +-
...do2-page-script.webauthn-supported.spec.ts | 11 +
.../abstractions/autofill-inline-menu-list.ts | 1 +
.../autofill-inline-menu-list.spec.ts.snap | 504 +++++++++++++++++-
.../list/autofill-inline-menu-list.spec.ts | 180 ++++++-
.../pages/list/autofill-inline-menu-list.ts | 302 ++++++++++-
.../overlay/inline-menu/pages/list/list.scss | 52 ++
...inline-menu-field-qualification.service.ts | 2 +
.../src/autofill/spec/autofill-mocks.ts | 5 +-
apps/browser/src/autofill/utils/svg-icons.ts | 15 +-
.../browser/src/background/main.background.ts | 4 +
.../browser/src/vault/fido2/webauthn-utils.ts | 1 +
libs/common/src/autofill/constants/index.ts | 2 +
...ido2-active-request-manager.abstraction.ts | 21 +
...fido2-authenticator.service.abstraction.ts | 13 +
.../fido2/fido2-client.service.abstraction.ts | 9 +
.../fido2-active-request-manager.spec.ts | 89 ++++
.../fido2/fido2-active-request-manager.ts | 109 ++++
.../fido2/fido2-authenticator.service.spec.ts | 16 +
.../fido2/fido2-authenticator.service.ts | 31 +-
.../fido2/fido2-client.service.spec.ts | 86 ++-
.../services/fido2/fido2-client.service.ts | 84 ++-
26 files changed, 1889 insertions(+), 119 deletions(-)
create mode 100644 libs/common/src/platform/abstractions/fido2/fido2-active-request-manager.abstraction.ts
create mode 100644 libs/common/src/platform/services/fido2/fido2-active-request-manager.spec.ts
create mode 100644 libs/common/src/platform/services/fido2/fido2-active-request-manager.ts
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index 69600b5da7c..9a69d5f1085 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -3926,6 +3926,18 @@
"data": {
"message": "Data"
},
+ "passkeys": {
+ "message": "Passkeys",
+ "description": "A section header for a list of passkeys. Used in the inline menu list."
+ },
+ "passwords": {
+ "message": "Passwords",
+ "description": "A section header for a list of passwords. Used in the inline menu list."
+ },
+ "logInWithPasskeyAriaLabel": {
+ "message": "Log in with passkey",
+ "description": "ARIA label for the inline menu button that logs in with a passkey."
+ },
"assign": {
"message": "Assign"
},
diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts
index 8122f5c4ed9..950f3b8e275 100644
--- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts
+++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts
@@ -1,5 +1,6 @@
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
+import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import AutofillPageDetails from "../../models/autofill-page-details";
import { PageDetail } from "../../services/abstractions/autofill.service";
@@ -132,6 +133,7 @@ export type OverlayPortMessage = {
direction?: string;
inlineMenuCipherId?: string;
addNewCipherType?: CipherType;
+ usePasskey?: boolean;
};
export type InlineMenuCipherData = {
@@ -142,7 +144,13 @@ export type InlineMenuCipherData = {
favorite: boolean;
icon: WebsiteIconData;
accountCreationFieldType?: string;
- login?: { username: string };
+ login?: {
+ username: string;
+ passkey: {
+ rpName: string;
+ userName: string;
+ } | null;
+ };
card?: string;
identity?: {
fullName: string;
@@ -150,6 +158,15 @@ export type InlineMenuCipherData = {
};
};
+export type BuildCipherDataParams = {
+ inlineMenuCipherId: string;
+ cipher: CipherView;
+ showFavicons?: boolean;
+ showInlineMenuAccountCreation?: boolean;
+ hasPasskey?: boolean;
+ identityData?: { fullName: string; username?: string };
+};
+
export type BackgroundMessageParam = {
message: OverlayBackgroundExtensionMessage;
};
diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts
index fe118868628..e29cc8331a2 100644
--- a/apps/browser/src/autofill/background/overlay.background.spec.ts
+++ b/apps/browser/src/autofill/background/overlay.background.spec.ts
@@ -17,6 +17,7 @@ import {
EnvironmentService,
Region,
} from "@bitwarden/common/platform/abstractions/environment.service";
+import { Fido2ClientService } from "@bitwarden/common/platform/abstractions/fido2/fido2-client.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
@@ -32,6 +33,7 @@ import { UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
+import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view";
import { BrowserApi } from "../../platform/browser/browser-api";
import { BrowserPlatformUtilsService } from "../../platform/services/platform-utils/browser-platform-utils.service";
@@ -85,6 +87,8 @@ describe("OverlayBackground", () => {
let autofillSettingsService: MockProxy;
let i18nService: MockProxy;
let platformUtilsService: MockProxy;
+ let availableAutofillCredentialsMock$: BehaviorSubject;
+ let fido2ClientService: MockProxy;
let selectedThemeMock$: BehaviorSubject;
let themeStateService: MockProxy;
let overlayBackground: OverlayBackground;
@@ -151,6 +155,10 @@ describe("OverlayBackground", () => {
autofillSettingsService.inlineMenuVisibility$ = inlineMenuVisibilityMock$;
i18nService = mock();
platformUtilsService = mock();
+ availableAutofillCredentialsMock$ = new BehaviorSubject([]);
+ fido2ClientService = mock({
+ availableAutofillCredentials$: (_tabId) => availableAutofillCredentialsMock$,
+ });
selectedThemeMock$ = new BehaviorSubject(ThemeType.Light);
themeStateService = mock();
themeStateService.selectedTheme$ = selectedThemeMock$;
@@ -164,6 +172,7 @@ describe("OverlayBackground", () => {
autofillSettingsService,
i18nService,
platformUtilsService,
+ fido2ClientService,
themeStateService,
);
portKeyForTabSpy = overlayBackground["portKeyForTab"];
@@ -699,28 +708,28 @@ describe("OverlayBackground", () => {
describe("updating the overlay ciphers", () => {
const url = "https://jest-testing-website.com";
const tab = createChromeTabMock({ url });
- const cipher1 = mock({
+ const loginCipher1 = mock({
id: "id-1",
localData: { lastUsedDate: 222 },
name: "name-1",
type: CipherType.Login,
login: { username: "username-1", uri: url },
});
- const cipher2 = mock({
+ const cardCipher = mock({
id: "id-2",
localData: { lastUsedDate: 222 },
name: "name-2",
type: CipherType.Card,
card: { subTitle: "subtitle-2" },
});
- const cipher3 = mock({
+ const loginCipher2 = mock({
id: "id-3",
localData: { lastUsedDate: 222 },
name: "name-3",
type: CipherType.Login,
login: { username: "username-3", uri: url },
});
- const cipher4 = mock({
+ const identityCipher = mock({
id: "id-4",
localData: { lastUsedDate: 222 },
name: "name-4",
@@ -732,6 +741,23 @@ describe("OverlayBackground", () => {
email: "email@example.com",
},
});
+ const passkeyCipher = mock({
+ id: "id-5",
+ localData: { lastUsedDate: 222 },
+ name: "name-5",
+ type: CipherType.Login,
+ login: {
+ username: "username-5",
+ uri: url,
+ fido2Credentials: [
+ mock({
+ credentialId: "credential-id",
+ rpName: "credential-name",
+ userName: "credential-username",
+ }),
+ ],
+ },
+ });
beforeEach(async () => {
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
@@ -764,7 +790,7 @@ describe("OverlayBackground", () => {
it("closes the inline menu on the focused field's tab if current tab is different", async () => {
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
- cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1, cipher2]);
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, cardCipher]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
const previousTab = mock({ id: 15 });
overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: 15 });
@@ -781,7 +807,7 @@ describe("OverlayBackground", () => {
it("queries all cipher types, sorts them by last used, and formats them for usage in the overlay", async () => {
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
- cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1, cipher2]);
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, cardCipher]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
await overlayBackground.updateOverlayCiphers();
@@ -794,8 +820,8 @@ describe("OverlayBackground", () => {
expect(cipherService.sortCiphersByLastUsedThenName).toHaveBeenCalled();
expect(overlayBackground["inlineMenuCiphers"]).toStrictEqual(
new Map([
- ["inline-menu-cipher-0", cipher2],
- ["inline-menu-cipher-1", cipher1],
+ ["inline-menu-cipher-0", cardCipher],
+ ["inline-menu-cipher-1", loginCipher1],
]),
);
});
@@ -803,7 +829,7 @@ describe("OverlayBackground", () => {
it("queries only login ciphers when not updating all cipher types", async () => {
overlayBackground["cardAndIdentityCiphers"] = new Set([]);
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
- cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher3, cipher1]);
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher2, loginCipher1]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
await overlayBackground.updateOverlayCiphers(false);
@@ -813,15 +839,15 @@ describe("OverlayBackground", () => {
expect(cipherService.sortCiphersByLastUsedThenName).toHaveBeenCalled();
expect(overlayBackground["inlineMenuCiphers"]).toStrictEqual(
new Map([
- ["inline-menu-cipher-0", cipher1],
- ["inline-menu-cipher-1", cipher3],
+ ["inline-menu-cipher-0", loginCipher1],
+ ["inline-menu-cipher-1", loginCipher2],
]),
);
});
it("queries all cipher types when the card and identity ciphers set is not built when only updating login ciphers", async () => {
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
- cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1, cipher2]);
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, cardCipher]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
await overlayBackground.updateOverlayCiphers(false);
@@ -834,15 +860,15 @@ describe("OverlayBackground", () => {
expect(cipherService.sortCiphersByLastUsedThenName).toHaveBeenCalled();
expect(overlayBackground["inlineMenuCiphers"]).toStrictEqual(
new Map([
- ["inline-menu-cipher-0", cipher2],
- ["inline-menu-cipher-1", cipher1],
+ ["inline-menu-cipher-0", cardCipher],
+ ["inline-menu-cipher-1", loginCipher1],
]),
);
});
it("posts an `updateOverlayListCiphers` message to the overlay list port, and send a `updateAutofillInlineMenuListCiphers` message to the tab indicating that the list of ciphers is populated", async () => {
overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id });
- cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1]);
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
@@ -851,10 +877,11 @@ describe("OverlayBackground", () => {
expect(listPortSpy.postMessage).toHaveBeenCalledWith({
command: "updateAutofillInlineMenuListCiphers",
showInlineMenuAccountCreation: false,
+ showPasskeysLabels: false,
ciphers: [
{
accountCreationFieldType: undefined,
- favorite: cipher1.favorite,
+ favorite: loginCipher1.favorite,
icon: {
fallbackImage: "images/bwi-globe.png",
icon: "bwi-globe",
@@ -864,9 +891,10 @@ describe("OverlayBackground", () => {
id: "inline-menu-cipher-0",
login: {
username: "username-1",
+ passkey: null,
},
name: "name-1",
- reprompt: cipher1.reprompt,
+ reprompt: loginCipher1.reprompt,
type: CipherType.Login,
},
],
@@ -878,7 +906,7 @@ describe("OverlayBackground", () => {
tabId: tab.id,
filledByCipherType: CipherType.Card,
});
- cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1, cipher2]);
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, cardCipher]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
@@ -887,10 +915,11 @@ describe("OverlayBackground", () => {
expect(listPortSpy.postMessage).toHaveBeenCalledWith({
command: "updateAutofillInlineMenuListCiphers",
showInlineMenuAccountCreation: false,
+ showPasskeysLabels: false,
ciphers: [
{
accountCreationFieldType: undefined,
- favorite: cipher2.favorite,
+ favorite: cardCipher.favorite,
icon: {
fallbackImage: "",
icon: "bwi-credit-card",
@@ -898,9 +927,9 @@ describe("OverlayBackground", () => {
imageEnabled: true,
},
id: "inline-menu-cipher-0",
- card: cipher2.card.subTitle,
- name: cipher2.name,
- reprompt: cipher2.reprompt,
+ card: cardCipher.card.subTitle,
+ name: cardCipher.name,
+ reprompt: cardCipher.reprompt,
type: CipherType.Card,
},
],
@@ -914,7 +943,7 @@ describe("OverlayBackground", () => {
accountCreationFieldType: "text",
showInlineMenuAccountCreation: true,
});
- cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher4, cipher2]);
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([identityCipher, cardCipher]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
@@ -923,10 +952,11 @@ describe("OverlayBackground", () => {
expect(listPortSpy.postMessage).toHaveBeenCalledWith({
command: "updateAutofillInlineMenuListCiphers",
showInlineMenuAccountCreation: true,
+ showPasskeysLabels: false,
ciphers: [
{
accountCreationFieldType: "text",
- favorite: cipher4.favorite,
+ favorite: identityCipher.favorite,
icon: {
fallbackImage: "",
icon: "bwi-id-card",
@@ -934,12 +964,12 @@ describe("OverlayBackground", () => {
imageEnabled: true,
},
id: "inline-menu-cipher-1",
- name: cipher4.name,
- reprompt: cipher4.reprompt,
+ name: identityCipher.name,
+ reprompt: identityCipher.reprompt,
type: CipherType.Identity,
identity: {
- fullName: `${cipher4.identity.firstName} ${cipher4.identity.lastName}`,
- username: cipher4.identity.username,
+ fullName: `${identityCipher.identity.firstName} ${identityCipher.identity.lastName}`,
+ username: identityCipher.identity.username,
},
},
],
@@ -952,7 +982,7 @@ describe("OverlayBackground", () => {
accountCreationFieldType: "text",
showInlineMenuAccountCreation: true,
});
- cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1, cipher4]);
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, identityCipher]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
@@ -961,10 +991,11 @@ describe("OverlayBackground", () => {
expect(listPortSpy.postMessage).toHaveBeenCalledWith({
command: "updateAutofillInlineMenuListCiphers",
showInlineMenuAccountCreation: true,
+ showPasskeysLabels: false,
ciphers: [
{
accountCreationFieldType: "text",
- favorite: cipher4.favorite,
+ favorite: identityCipher.favorite,
icon: {
fallbackImage: "",
icon: "bwi-id-card",
@@ -972,17 +1003,17 @@ describe("OverlayBackground", () => {
imageEnabled: true,
},
id: "inline-menu-cipher-0",
- name: cipher4.name,
- reprompt: cipher4.reprompt,
+ name: identityCipher.name,
+ reprompt: identityCipher.reprompt,
type: CipherType.Identity,
identity: {
- fullName: `${cipher4.identity.firstName} ${cipher4.identity.lastName}`,
- username: cipher4.identity.username,
+ fullName: `${identityCipher.identity.firstName} ${identityCipher.identity.lastName}`,
+ username: identityCipher.identity.username,
},
},
{
accountCreationFieldType: "text",
- favorite: cipher1.favorite,
+ favorite: loginCipher1.favorite,
icon: {
fallbackImage: "images/bwi-globe.png",
icon: "bwi-globe",
@@ -991,10 +1022,11 @@ describe("OverlayBackground", () => {
},
id: "inline-menu-cipher-1",
login: {
- username: cipher1.login.username,
+ username: loginCipher1.login.username,
+ passkey: null,
},
- name: cipher1.name,
- reprompt: cipher1.reprompt,
+ name: loginCipher1.name,
+ reprompt: loginCipher1.reprompt,
type: CipherType.Login,
},
],
@@ -1018,7 +1050,7 @@ describe("OverlayBackground", () => {
},
});
cipherService.getAllDecryptedForUrl.mockResolvedValue([
- cipher4,
+ identityCipher,
identityCipherWithoutUsername,
]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
@@ -1029,10 +1061,11 @@ describe("OverlayBackground", () => {
expect(listPortSpy.postMessage).toHaveBeenCalledWith({
command: "updateAutofillInlineMenuListCiphers",
showInlineMenuAccountCreation: true,
+ showPasskeysLabels: false,
ciphers: [
{
accountCreationFieldType: "email",
- favorite: cipher4.favorite,
+ favorite: identityCipher.favorite,
icon: {
fallbackImage: "",
icon: "bwi-id-card",
@@ -1040,12 +1073,12 @@ describe("OverlayBackground", () => {
imageEnabled: true,
},
id: "inline-menu-cipher-1",
- name: cipher4.name,
- reprompt: cipher4.reprompt,
+ name: identityCipher.name,
+ reprompt: identityCipher.reprompt,
type: CipherType.Identity,
identity: {
- fullName: `${cipher4.identity.firstName} ${cipher4.identity.lastName}`,
- username: cipher4.identity.email,
+ fullName: `${identityCipher.identity.firstName} ${identityCipher.identity.lastName}`,
+ username: identityCipher.identity.email,
},
},
],
@@ -1058,7 +1091,7 @@ describe("OverlayBackground", () => {
accountCreationFieldType: "password",
showInlineMenuAccountCreation: true,
});
- cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher4]);
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([identityCipher]);
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
@@ -1067,10 +1100,89 @@ describe("OverlayBackground", () => {
expect(listPortSpy.postMessage).toHaveBeenCalledWith({
command: "updateAutofillInlineMenuListCiphers",
showInlineMenuAccountCreation: true,
+ showPasskeysLabels: false,
ciphers: [],
});
});
});
+
+ it("adds available passkey ciphers to the inline menu", async () => {
+ availableAutofillCredentialsMock$.next(passkeyCipher.login.fido2Credentials);
+ overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({
+ tabId: tab.id,
+ filledByCipherType: CipherType.Login,
+ });
+ cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, passkeyCipher]);
+ cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
+ getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
+
+ await overlayBackground.updateOverlayCiphers();
+
+ expect(listPortSpy.postMessage).toHaveBeenCalledWith({
+ command: "updateAutofillInlineMenuListCiphers",
+ ciphers: [
+ {
+ id: "inline-menu-cipher-0",
+ name: passkeyCipher.name,
+ type: CipherType.Login,
+ reprompt: passkeyCipher.reprompt,
+ favorite: passkeyCipher.favorite,
+ icon: {
+ fallbackImage: "images/bwi-globe.png",
+ icon: "bwi-globe",
+ image: "https://icons.bitwarden.com//jest-testing-website.com/icon.png",
+ imageEnabled: true,
+ },
+ accountCreationFieldType: undefined,
+ login: {
+ username: passkeyCipher.login.username,
+ passkey: {
+ rpName: passkeyCipher.login.fido2Credentials[0].rpName,
+ userName: passkeyCipher.login.fido2Credentials[0].userName,
+ },
+ },
+ },
+ {
+ id: "inline-menu-cipher-0",
+ name: passkeyCipher.name,
+ type: CipherType.Login,
+ reprompt: passkeyCipher.reprompt,
+ favorite: passkeyCipher.favorite,
+ icon: {
+ fallbackImage: "images/bwi-globe.png",
+ icon: "bwi-globe",
+ image: "https://icons.bitwarden.com//jest-testing-website.com/icon.png",
+ imageEnabled: true,
+ },
+ accountCreationFieldType: undefined,
+ login: {
+ username: passkeyCipher.login.username,
+ passkey: null,
+ },
+ },
+ {
+ id: "inline-menu-cipher-1",
+ name: loginCipher1.name,
+ type: CipherType.Login,
+ reprompt: loginCipher1.reprompt,
+ favorite: loginCipher1.favorite,
+ icon: {
+ fallbackImage: "images/bwi-globe.png",
+ icon: "bwi-globe",
+ image: "https://icons.bitwarden.com//jest-testing-website.com/icon.png",
+ imageEnabled: true,
+ },
+ accountCreationFieldType: undefined,
+ login: {
+ username: loginCipher1.login.username,
+ passkey: null,
+ },
+ },
+ ],
+ showInlineMenuAccountCreation: false,
+ showPasskeysLabels: true,
+ });
+ });
});
describe("extension message handlers", () => {
@@ -1562,6 +1674,7 @@ describe("OverlayBackground", () => {
command: "updateAutofillInlineMenuListCiphers",
ciphers: [],
showInlineMenuAccountCreation: true,
+ showPasskeysLabels: false,
});
});
@@ -2660,6 +2773,41 @@ describe("OverlayBackground", () => {
expect(copyToClipboardSpy).toHaveBeenCalledWith("totp-code");
});
+
+ it("triggers passkey authentication through mediated conditional UI", async () => {
+ const fido2Credential = mock({ credentialId: "credential-id" });
+ const cipher1 = mock({
+ id: "inline-menu-cipher-1",
+ login: {
+ username: "username1",
+ password: "password1",
+ fido2Credentials: [fido2Credential],
+ },
+ });
+ overlayBackground["inlineMenuCiphers"] = new Map([["inline-menu-cipher-1", cipher1]]);
+ const pageDetailsForTab = {
+ frameId: sender.frameId,
+ tab: sender.tab,
+ details: pageDetails,
+ };
+ overlayBackground["pageDetailsForTab"][sender.tab.id] = new Map([
+ [sender.frameId, pageDetailsForTab],
+ ]);
+ autofillService.isPasswordRepromptRequired.mockResolvedValue(false);
+
+ sendPortMessage(listMessageConnectorSpy, {
+ command: "fillAutofillInlineMenuCipher",
+ inlineMenuCipherId: "inline-menu-cipher-1",
+ usePasskey: true,
+ portKey,
+ });
+ await flushPromises();
+
+ expect(fido2ClientService.autofillCredential).toHaveBeenCalledWith(
+ sender.tab.id,
+ fido2Credential.credentialId,
+ );
+ });
});
describe("addNewVaultItem message handler", () => {
diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts
index 8c4dac56d50..3bb80b09b2e 100644
--- a/apps/browser/src/autofill/background/overlay.background.ts
+++ b/apps/browser/src/autofill/background/overlay.background.ts
@@ -1,5 +1,12 @@
-import { firstValueFrom, merge, Subject, throttleTime } from "rxjs";
-import { debounceTime, switchMap } from "rxjs/operators";
+import {
+ firstValueFrom,
+ merge,
+ ReplaySubject,
+ Subject,
+ throttleTime,
+ switchMap,
+ debounceTime,
+} from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
@@ -11,6 +18,7 @@ import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/s
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
+import { Fido2ClientService } from "@bitwarden/common/platform/abstractions/fido2/fido2-client.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -21,6 +29,7 @@ import { CipherType } from "@bitwarden/common/vault/enums";
import { buildCipherIcon } from "@bitwarden/common/vault/icon/build-cipher-icon";
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
+import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view";
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";
@@ -41,6 +50,7 @@ import { generateRandomChars } from "../utils";
import { LockedVaultPendingNotificationsData } from "./abstractions/notification.background";
import {
+ BuildCipherDataParams,
CloseInlineMenuMessage,
CurrentAddNewItemData,
FocusedFieldData,
@@ -66,6 +76,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private readonly openUnlockPopout = openUnlockPopout;
private readonly openViewVaultItemPopout = openViewVaultItemPopout;
private readonly openAddEditVaultItemPopout = openAddEditVaultItemPopout;
+ private readonly storeInlineMenuFido2CredentialsSubject = new ReplaySubject(1);
private pageDetailsForTab: PageDetailsForTab = {};
private subFrameOffsetsForTab: SubFrameOffsetsForTab = {};
private portKeyForTab: Record = {};
@@ -73,6 +84,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private inlineMenuButtonPort: chrome.runtime.Port;
private inlineMenuListPort: chrome.runtime.Port;
private inlineMenuCiphers: Map = new Map();
+ private inlineMenuFido2Credentials: Set = new Set();
private inlineMenuPageTranslations: Record;
private inlineMenuPosition: InlineMenuPosition = {};
private cardAndIdentityCiphers: Set | null = null;
@@ -91,6 +103,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private isFieldCurrentlyFilling: boolean = false;
private isInlineMenuButtonVisible: boolean = false;
private isInlineMenuListVisible: boolean = false;
+ private showPasskeysLabelsWithinInlineMenu: boolean = false;
private iconsServerUrl: string;
private readonly extensionMessageHandlers: OverlayBackgroundExtensionMessageHandlers = {
autofillOverlayElementClosed: ({ message, sender }) =>
@@ -159,6 +172,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private autofillSettingsService: AutofillSettingsServiceAbstraction,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
+ private fido2ClientService: Fido2ClientService,
private themeStateService: ThemeStateService,
) {
this.initOverlayEventObservables();
@@ -178,6 +192,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* Initializes event observables that handle events which affect the overlay's behavior.
*/
private initOverlayEventObservables() {
+ this.storeInlineMenuFido2CredentialsSubject
+ .pipe(switchMap((tabId) => this.fido2ClientService.availableAutofillCredentials$(tabId)))
+ .subscribe((credentials) => this.storeInlineMenuFido2Credentials(credentials));
this.repositionInlineMenuSubject
.pipe(
debounceTime(1000),
@@ -252,6 +269,13 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.closeInlineMenuAfterCiphersUpdate().catch((error) => this.logService.error(error));
}
+ if (!currentTab) {
+ return;
+ }
+
+ this.inlineMenuFido2Credentials.clear();
+ this.storeInlineMenuFido2CredentialsSubject.next(currentTab.id);
+
this.inlineMenuCiphers = new Map();
const ciphersViews = await this.getCipherViews(currentTab, updateAllCipherTypes);
for (let cipherIndex = 0; cipherIndex < ciphersViews.length; cipherIndex++) {
@@ -263,6 +287,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
command: "updateAutofillInlineMenuListCiphers",
ciphers,
showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(),
+ showPasskeysLabels: this.showPasskeysLabelsWithinInlineMenu,
});
}
@@ -280,9 +305,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
return this.getAllCipherTypeViews(currentTab);
}
- const cipherViews = (
- await this.cipherService.getAllDecryptedForUrl(currentTab?.url || "")
- ).sort((a, b) => this.cipherService.sortCiphersByLastUsedThenName(a, b));
+ const cipherViews = (await this.cipherService.getAllDecryptedForUrl(currentTab.url || "")).sort(
+ (a, b) => this.cipherService.sortCiphersByLastUsedThenName(a, b),
+ );
return this.cardAndIdentityCiphers
? cipherViews.concat(...this.cardAndIdentityCiphers)
@@ -301,7 +326,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.cardAndIdentityCiphers.clear();
const cipherViews = (
- await this.cipherService.getAllDecryptedForUrl(currentTab.url, [
+ await this.cipherService.getAllDecryptedForUrl(currentTab.url || "", [
CipherType.Card,
CipherType.Identity,
])
@@ -331,6 +356,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const showFavicons = await firstValueFrom(this.domainSettingsService.showFavicons$);
const inlineMenuCiphersArray = Array.from(this.inlineMenuCiphers);
let inlineMenuCipherData: InlineMenuCipherData[];
+ this.showPasskeysLabelsWithinInlineMenu = false;
if (this.showInlineMenuAccountCreation()) {
inlineMenuCipherData = this.buildInlineMenuAccountCreationCiphers(
@@ -363,7 +389,12 @@ export class OverlayBackground implements OverlayBackgroundInterface {
if (cipher.type === CipherType.Login) {
accountCreationLoginCiphers.push(
- this.buildCipherData(inlineMenuCipherId, cipher, showFavicons, true),
+ this.buildCipherData({
+ inlineMenuCipherId,
+ cipher,
+ showFavicons,
+ showInlineMenuAccountCreation: true,
+ }),
);
continue;
}
@@ -378,7 +409,13 @@ export class OverlayBackground implements OverlayBackgroundInterface {
}
inlineMenuCipherData.push(
- this.buildCipherData(inlineMenuCipherId, cipher, showFavicons, true, identity),
+ this.buildCipherData({
+ inlineMenuCipherId,
+ cipher,
+ showFavicons,
+ showInlineMenuAccountCreation: true,
+ identityData: identity,
+ }),
);
}
@@ -400,6 +437,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
showFavicons: boolean,
) {
const inlineMenuCipherData: InlineMenuCipherData[] = [];
+ const passkeyCipherData: InlineMenuCipherData[] = [];
for (let cipherIndex = 0; cipherIndex < inlineMenuCiphersArray.length; cipherIndex++) {
const [inlineMenuCipherId, cipher] = inlineMenuCiphersArray[cipherIndex];
@@ -407,12 +445,43 @@ export class OverlayBackground implements OverlayBackgroundInterface {
continue;
}
- inlineMenuCipherData.push(this.buildCipherData(inlineMenuCipherId, cipher, showFavicons));
+ if (this.showCipherAsPasskey(cipher)) {
+ passkeyCipherData.push(
+ this.buildCipherData({
+ inlineMenuCipherId,
+ cipher,
+ showFavicons,
+ hasPasskey: true,
+ }),
+ );
+ }
+
+ inlineMenuCipherData.push(this.buildCipherData({ inlineMenuCipherId, cipher, showFavicons }));
+ }
+
+ if (passkeyCipherData.length) {
+ this.showPasskeysLabelsWithinInlineMenu =
+ passkeyCipherData.length > 0 && inlineMenuCipherData.length > 0;
+ return passkeyCipherData.concat(inlineMenuCipherData);
}
return inlineMenuCipherData;
}
+ /**
+ * Identifies whether we should show the cipher as a passkey in the inline menu list.
+ *
+ * @param cipher - The cipher to check
+ */
+ private showCipherAsPasskey(cipher: CipherView): boolean {
+ return (
+ cipher.type === CipherType.Login &&
+ cipher.login.fido2Credentials?.length > 0 &&
+ (this.inlineMenuFido2Credentials.size === 0 ||
+ this.inlineMenuFido2Credentials.has(cipher.login.fido2Credentials[0].credentialId))
+ );
+ }
+
/**
* Builds the cipher data for the inline menu list.
*
@@ -420,15 +489,17 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* @param cipher - The cipher to build data for
* @param showFavicons - Identifies whether favicons should be shown
* @param showInlineMenuAccountCreation - Identifies whether the inline menu is for account creation
+ * @param hasPasskey - Identifies whether the cipher has a FIDO2 credential
* @param identityData - Pre-created identity data
*/
- private buildCipherData(
- inlineMenuCipherId: string,
- cipher: CipherView,
- showFavicons: boolean,
- showInlineMenuAccountCreation: boolean = false,
- identityData?: { fullName: string; username?: string },
- ): InlineMenuCipherData {
+ private buildCipherData({
+ inlineMenuCipherId,
+ cipher,
+ showFavicons,
+ showInlineMenuAccountCreation,
+ hasPasskey,
+ identityData,
+ }: BuildCipherDataParams): InlineMenuCipherData {
const inlineMenuData: InlineMenuCipherData = {
id: inlineMenuCipherId,
name: cipher.name,
@@ -440,7 +511,15 @@ export class OverlayBackground implements OverlayBackgroundInterface {
};
if (cipher.type === CipherType.Login) {
- inlineMenuData.login = { username: cipher.login.username };
+ inlineMenuData.login = {
+ username: cipher.login.username,
+ passkey: hasPasskey
+ ? {
+ rpName: cipher.login.fido2Credentials[0].rpName,
+ userName: cipher.login.fido2Credentials[0].userName,
+ }
+ : null,
+ };
return inlineMenuData;
}
@@ -512,6 +591,17 @@ export class OverlayBackground implements OverlayBackgroundInterface {
return this.inlineMenuCiphers.size === 0;
}
+ /**
+ * Stores the credential ids associated with a FIDO2 conditional mediated ui request.
+ *
+ * @param credentials - The FIDO2 credentials to store
+ */
+ private storeInlineMenuFido2Credentials(credentials: Fido2CredentialView[]) {
+ credentials
+ .map((credential) => credential.credentialId)
+ .forEach((credentialId) => this.inlineMenuFido2Credentials.add(credentialId));
+ }
+
/**
* Gets the currently focused field and closes the inline menu on that tab.
*/
@@ -749,10 +839,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* the selected cipher at the top of the list of ciphers.
*
* @param inlineMenuCipherId - Cipher ID corresponding to the inlineMenuCiphers map. Does not correspond to the actual cipher's ID.
+ * @param usePasskey - Identifies whether the cipher has a FIDO2 credential
* @param sender - The sender of the port message
*/
private async fillInlineMenuCipher(
- { inlineMenuCipherId }: OverlayPortMessage,
+ { inlineMenuCipherId, usePasskey }: OverlayPortMessage,
{ sender }: chrome.runtime.Port,
) {
const pageDetails = this.pageDetailsForTab[sender.tab.id];
@@ -762,6 +853,16 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const cipher = this.inlineMenuCiphers.get(inlineMenuCipherId);
+ if (usePasskey && cipher.login?.hasFido2Credentials) {
+ await this.fido2ClientService.autofillCredential(
+ sender.tab.id,
+ cipher.login.fido2Credentials[0].credentialId,
+ );
+ this.updateLastUsedInlineMenuCipher(inlineMenuCipherId, cipher);
+
+ return;
+ }
+
if (await this.autofillService.isPasswordRepromptRequired(cipher, sender.tab)) {
return;
}
@@ -777,6 +878,16 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.platformUtilsService.copyToClipboard(totpCode);
}
+ this.updateLastUsedInlineMenuCipher(inlineMenuCipherId, cipher);
+ }
+
+ /**
+ * Sets the most recently used cipher at the top of the list of ciphers.
+ *
+ * @param inlineMenuCipherId - The ID of the inline menu cipher
+ * @param cipher - The cipher to set as the most recently used
+ */
+ private updateLastUsedInlineMenuCipher(inlineMenuCipherId: string, cipher: CipherView) {
this.inlineMenuCiphers = new Map([[inlineMenuCipherId, cipher], ...this.inlineMenuCiphers]);
}
@@ -1163,6 +1274,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
command: "updateAutofillInlineMenuListCiphers",
ciphers: await this.getInlineMenuCipherData(),
showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(),
+ showPasskeysLabels: this.showPasskeysLabelsWithinInlineMenu,
});
}
@@ -1214,6 +1326,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private async openInlineMenu(isFocusingFieldElement = false, isOpeningFullInlineMenu = false) {
this.clearDelayedInlineMenuClosure();
const currentTab = await BrowserApi.getTabFromCurrentWindowId();
+ if (!currentTab) {
+ return;
+ }
await BrowserApi.tabSendMessage(
currentTab,
@@ -1224,8 +1339,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
authStatus: await this.getAuthStatus(),
},
{
- frameId:
- this.focusedFieldData?.tabId === currentTab?.id ? this.focusedFieldData.frameId : 0,
+ frameId: this.focusedFieldData?.tabId === currentTab.id ? this.focusedFieldData.frameId : 0,
},
);
}
@@ -1367,6 +1481,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
newIdentity: this.i18nService.translate("newIdentity"),
addNewIdentityItem: this.i18nService.translate("addNewIdentityItemAria"),
cardNumberEndsWith: this.i18nService.translate("cardNumberEndsWith"),
+ passkeys: this.i18nService.translate("passkeys"),
+ passwords: this.i18nService.translate("passwords"),
+ logInWithPasskey: this.i18nService.translate("logInWithPasskeyAriaLabel"),
};
}
@@ -2064,6 +2181,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
: AutofillOverlayPort.ButtonMessageConnector,
filledByCipherType: this.focusedFieldData?.filledByCipherType,
showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(),
+ showPasskeysLabels: this.showPasskeysLabelsWithinInlineMenu,
});
this.updateInlineMenuPosition(
{
diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script.ts
index 5f91e6c0813..7275ced37ba 100644
--- a/apps/browser/src/autofill/fido2/content/fido2-page-script.ts
+++ b/apps/browser/src/autofill/fido2/content/fido2-page-script.ts
@@ -1,5 +1,3 @@
-import { FallbackRequestedError } from "@bitwarden/common/platform/abstractions/fido2/fido2-client.service.abstraction";
-
import { WebauthnUtils } from "../../../vault/fido2/webauthn-utils";
import { MessageType } from "./messaging/message";
@@ -126,13 +124,47 @@ import { Messenger } from "./messaging/messenger";
return await browserCredentials.get(options);
}
+ const abortSignal = options?.signal || new AbortController().signal;
const fallbackSupported = browserNativeWebauthnSupport;
- try {
- if (options?.mediation && options.mediation !== "optional") {
- throw new FallbackRequestedError();
- }
+ if (options?.mediation && options.mediation === "conditional") {
+ const internalAbortControllers = [new AbortController(), new AbortController()];
+ const bitwardenResponse = async (internalAbortController: AbortController) => {
+ try {
+ const response = await messenger.request(
+ {
+ type: MessageType.CredentialGetRequest,
+ data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported),
+ },
+ internalAbortController.signal,
+ );
+ if (response.type !== MessageType.CredentialGetResponse) {
+ throw new Error("Something went wrong.");
+ }
+ return WebauthnUtils.mapCredentialAssertResult(response.result);
+ } catch {
+ // Ignoring error
+ }
+ };
+ const browserResponse = (internalAbortController: AbortController) =>
+ browserCredentials.get({ ...options, signal: internalAbortController.signal });
+ const abortListener = () => {
+ internalAbortControllers.forEach((controller) => controller.abort());
+ };
+ abortSignal.addEventListener("abort", abortListener);
+
+ const response = await Promise.race([
+ bitwardenResponse(internalAbortControllers[0]),
+ browserResponse(internalAbortControllers[1]),
+ ]);
+ abortSignal.removeEventListener("abort", abortListener);
+ internalAbortControllers.forEach((controller) => controller.abort());
+
+ return response;
+ }
+
+ try {
const response = await messenger.request(
{
type: MessageType.CredentialGetRequest,
diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-supported.spec.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-supported.spec.ts
index 292d0e01182..21f5a1d701a 100644
--- a/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-supported.spec.ts
+++ b/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-supported.spec.ts
@@ -128,6 +128,17 @@ describe("Fido2 page script with native WebAuthn support", () => {
mockCredentialAssertResult,
);
});
+
+ it("initiates a conditional mediated webauth request", async () => {
+ mockCredentialRequestOptions.mediation = "conditional";
+ mockCredentialRequestOptions.signal = new AbortController().signal;
+
+ await navigator.credentials.get(mockCredentialRequestOptions);
+
+ expect(WebauthnUtils.mapCredentialAssertResult).toHaveBeenCalledWith(
+ mockCredentialAssertResult,
+ );
+ });
});
describe("destroy", () => {
diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts
index 090fb7887c9..ea584165b4d 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts
+++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts
@@ -18,6 +18,7 @@ export type InitAutofillInlineMenuListMessage = AutofillInlineMenuListMessage &
ciphers?: InlineMenuCipherData[];
filledByCipherType?: CipherType;
showInlineMenuAccountCreation?: boolean;
+ showPasskeysLabels?: boolean;
portKey: string;
};
diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap
index a8a4d5c4a78..93d757fc51e 100644
--- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap
+++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap
@@ -478,7 +478,6 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f
class="cipher-container"
>