diff --git a/apps/browser/src/auth/popup/change-password/extension-change-password.service.spec.ts b/apps/browser/src/auth/popup/change-password/extension-change-password.service.spec.ts new file mode 100644 index 00000000000..a6a6b905218 --- /dev/null +++ b/apps/browser/src/auth/popup/change-password/extension-change-password.service.spec.ts @@ -0,0 +1,50 @@ +import { MockProxy, mock } from "jest-mock-extended"; + +import { ChangePasswordService } from "@bitwarden/angular/auth/password-management/change-password"; +import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { KeyService } from "@bitwarden/key-management"; + +import { BrowserApi } from "../../../platform/browser/browser-api"; +import BrowserPopupUtils from "../../../platform/browser/browser-popup-utils"; + +import { ExtensionChangePasswordService } from "./extension-change-password.service"; + +describe("ExtensionChangePasswordService", () => { + let keyService: MockProxy; + let masterPasswordApiService: MockProxy; + let masterPasswordService: MockProxy; + let window: MockProxy; + + let changePasswordService: ChangePasswordService; + + beforeEach(() => { + keyService = mock(); + masterPasswordApiService = mock(); + masterPasswordService = mock(); + window = mock(); + + changePasswordService = new ExtensionChangePasswordService( + keyService, + masterPasswordApiService, + masterPasswordService, + window, + ); + }); + + it("should instantiate the service", () => { + expect(changePasswordService).toBeDefined(); + }); + + it("should close the browser extension popout", () => { + const closePopupSpy = jest.spyOn(BrowserApi, "closePopup"); + const browserPopupUtilsInPopupSpy = jest + .spyOn(BrowserPopupUtils, "inPopout") + .mockReturnValue(true); + + changePasswordService.closeBrowserExtensionPopout?.(); + + expect(closePopupSpy).toHaveBeenCalledWith(window); + expect(browserPopupUtilsInPopupSpy).toHaveBeenCalledWith(window); + }); +}); diff --git a/apps/browser/src/auth/popup/change-password/extension-change-password.service.ts b/apps/browser/src/auth/popup/change-password/extension-change-password.service.ts new file mode 100644 index 00000000000..dd2ce48d27a --- /dev/null +++ b/apps/browser/src/auth/popup/change-password/extension-change-password.service.ts @@ -0,0 +1,29 @@ +import { + DefaultChangePasswordService, + ChangePasswordService, +} from "@bitwarden/angular/auth/password-management/change-password"; +import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { KeyService } from "@bitwarden/key-management"; + +import { BrowserApi } from "../../../platform/browser/browser-api"; +import BrowserPopupUtils from "../../../platform/browser/browser-popup-utils"; + +export class ExtensionChangePasswordService + extends DefaultChangePasswordService + implements ChangePasswordService +{ + constructor( + protected keyService: KeyService, + protected masterPasswordApiService: MasterPasswordApiService, + protected masterPasswordService: InternalMasterPasswordServiceAbstraction, + private win: Window, + ) { + super(keyService, masterPasswordApiService, masterPasswordService); + } + closeBrowserExtensionPopout(): void { + if (BrowserPopupUtils.inPopout(this.win)) { + BrowserApi.closePopup(this.win); + } + } +} diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 3887c8c8b12..509f7554aef 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -5,6 +5,7 @@ import { merge, of, Subject } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; import { DeviceManagementComponentServiceAbstraction } from "@bitwarden/angular/auth/device-management/device-management-component.service.abstraction"; +import { ChangePasswordService } from "@bitwarden/angular/auth/password-management/change-password"; import { AngularThemingService } from "@bitwarden/angular/platform/services/theming/angular-theming.service"; import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; @@ -45,6 +46,7 @@ import { AccountService as AccountServiceAbstraction, } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { @@ -143,6 +145,7 @@ import { import { AccountSwitcherService } from "../../auth/popup/account-switching/services/account-switcher.service"; import { ForegroundLockService } from "../../auth/popup/accounts/foreground-lock.service"; +import { ExtensionChangePasswordService } from "../../auth/popup/change-password/extension-change-password.service"; import { ExtensionLoginComponentService } from "../../auth/popup/login/extension-login-component.service"; import { ExtensionSsoComponentService } from "../../auth/popup/login/extension-sso-component.service"; import { ExtensionLogoutService } from "../../auth/popup/logout/extension-logout.service"; @@ -664,6 +667,11 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultSshImportPromptService, deps: [DialogService, ToastService, PlatformUtilsService, I18nServiceAbstraction], }), + safeProvider({ + provide: ChangePasswordService, + useClass: ExtensionChangePasswordService, + deps: [KeyService, MasterPasswordApiService, InternalMasterPasswordServiceAbstraction, WINDOW], + }), safeProvider({ provide: NotificationsService, useClass: ForegroundNotificationsService, diff --git a/libs/angular/src/auth/password-management/change-password/change-password.component.ts b/libs/angular/src/auth/password-management/change-password/change-password.component.ts index 02738d33321..7bb9584e934 100644 --- a/libs/angular/src/auth/password-management/change-password/change-password.component.ts +++ b/libs/angular/src/auth/password-management/change-password/change-password.component.ts @@ -178,6 +178,9 @@ export class ChangePasswordComponent implements OnInit { // TODO: PM-23515 eventually use the logout service instead of messaging service once it is available without circular dependencies this.messagingService.send("logout"); + + // Close the popout if we are in a browser extension popout. + this.changePasswordService.closeBrowserExtensionPopout?.(); } } catch (error) { this.logService.error(error); diff --git a/libs/angular/src/auth/password-management/change-password/change-password.service.abstraction.ts b/libs/angular/src/auth/password-management/change-password/change-password.service.abstraction.ts index 2fd3bbae67a..1d6d789cdc5 100644 --- a/libs/angular/src/auth/password-management/change-password/change-password.service.abstraction.ts +++ b/libs/angular/src/auth/password-management/change-password/change-password.service.abstraction.ts @@ -59,4 +59,10 @@ export abstract class ChangePasswordService { * - Currently only used on the web change password service. */ clearDeeplinkState?: () => Promise; + + /** + * Optional method that closes the browser extension popout if in a popout + * If not in a popout, does nothing. + */ + abstract closeBrowserExtensionPopout?(): void; }