From c5be837b51c556c2e9614d188a27c76682edb172 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Thu, 10 Jul 2025 15:00:49 -0400 Subject: [PATCH] chore(feature-flag) [PM-22604] Remove 2FA persistence feature flag * Removed flag. * Fixed tests to no longer reference flag. * Fixed test. * Removed duplicate test class. * Moved files into folders for yubikey and authenticator * Removed TwoFactorAuthEmailComponentService since it is no longer needed * Removed export * Fixed export --- ...actor-auth-email-component.service.spec.ts | 112 ------------ ...two-factor-auth-email-component.service.ts | 49 ------ .../src/popup/services/services.module.ts | 7 - .../src/services/jslib-services.module.ts | 7 - .../two-factor-auth/child-components/index.ts | 1 - ...o-factor-auth-authenticator.component.html | 0 ...two-factor-auth-authenticator.component.ts | 0 ...two-factor-auth-email-component.service.ts | 6 - .../two-factor-auth-email/index.ts | 2 - ...auth-component-email-cache.service.spec.ts | 165 ------------------ ...auth-email-component-cache.service.spec.ts | 78 +-------- ...ctor-auth-email-component-cache.service.ts | 27 --- ...two-factor-auth-email-component.service.ts | 10 -- .../two-factor-auth-email.component.ts | 5 - .../two-factor-auth-yubikey.component.html | 0 .../two-factor-auth-yubikey.component.ts | 0 ...actor-auth-component-cache.service.spec.ts | 84 +-------- ...two-factor-auth-component-cache.service.ts | 27 --- .../two-factor-auth.component.spec.ts | 1 - .../two-factor-auth.component.ts | 7 +- libs/common/src/enums/feature-flag.enum.ts | 2 - 21 files changed, 9 insertions(+), 581 deletions(-) delete mode 100644 apps/browser/src/auth/services/extension-two-factor-auth-email-component.service.spec.ts delete mode 100644 apps/browser/src/auth/services/extension-two-factor-auth-email-component.service.ts rename libs/auth/src/angular/two-factor-auth/child-components/{ => two-factor-auth-authenticator}/two-factor-auth-authenticator.component.html (100%) rename libs/auth/src/angular/two-factor-auth/child-components/{ => two-factor-auth-authenticator}/two-factor-auth-authenticator.component.ts (100%) delete mode 100644 libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/default-two-factor-auth-email-component.service.ts delete mode 100644 libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/index.ts delete mode 100644 libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-component-email-cache.service.spec.ts delete mode 100644 libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component.service.ts rename libs/auth/src/angular/two-factor-auth/child-components/{ => two-factor-auth-yubikey}/two-factor-auth-yubikey.component.html (100%) rename libs/auth/src/angular/two-factor-auth/child-components/{ => two-factor-auth-yubikey}/two-factor-auth-yubikey.component.ts (100%) diff --git a/apps/browser/src/auth/services/extension-two-factor-auth-email-component.service.spec.ts b/apps/browser/src/auth/services/extension-two-factor-auth-email-component.service.spec.ts deleted file mode 100644 index 432d00047a2..00000000000 --- a/apps/browser/src/auth/services/extension-two-factor-auth-email-component.service.spec.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { MockProxy, mock } from "jest-mock-extended"; - -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { DialogService } from "@bitwarden/components"; - -// Must mock modules before importing -jest.mock("../popup/utils/auth-popout-window", () => { - const originalModule = jest.requireActual("../popup/utils/auth-popout-window"); - - return { - ...originalModule, // avoid losing the original module's exports - openTwoFactorAuthEmailPopout: jest.fn(), - }; -}); - -jest.mock("../../platform/browser/browser-popup-utils", () => ({ - inPopup: jest.fn(), -})); - -// FIXME (PM-22628): Popup imports are forbidden in background -// eslint-disable-next-line no-restricted-imports -import { openTwoFactorAuthEmailPopout } from "../../auth/popup/utils/auth-popout-window"; -import BrowserPopupUtils from "../../platform/browser/browser-popup-utils"; - -import { ExtensionTwoFactorAuthEmailComponentService } from "./extension-two-factor-auth-email-component.service"; - -describe("ExtensionTwoFactorAuthEmailComponentService", () => { - let extensionTwoFactorAuthEmailComponentService: ExtensionTwoFactorAuthEmailComponentService; - - let dialogService: MockProxy; - let window: MockProxy; - let configService: MockProxy; - - beforeEach(() => { - jest.clearAllMocks(); - - dialogService = mock(); - window = mock(); - configService = mock(); - - extensionTwoFactorAuthEmailComponentService = new ExtensionTwoFactorAuthEmailComponentService( - dialogService, - window, - configService, - ); - }); - - describe("openPopoutIfApprovedForEmail2fa", () => { - it("should open a popout if the user confirms the warning to popout the extension when in the popup", async () => { - // Arrange - configService.getFeatureFlag.mockResolvedValue(false); - dialogService.openSimpleDialog.mockResolvedValue(true); - - jest.spyOn(BrowserPopupUtils, "inPopup").mockReturnValue(true); - - // Act - await extensionTwoFactorAuthEmailComponentService.openPopoutIfApprovedForEmail2fa(); - - // Assert - expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({ - title: { key: "warning" }, - content: { key: "popup2faCloseMessage" }, - type: "warning", - }); - - expect(openTwoFactorAuthEmailPopout).toHaveBeenCalled(); - }); - - it("should not open a popout if the user cancels the warning to popout the extension when in the popup", async () => { - // Arrange - configService.getFeatureFlag.mockResolvedValue(false); - dialogService.openSimpleDialog.mockResolvedValue(false); - - jest.spyOn(BrowserPopupUtils, "inPopup").mockReturnValue(true); - - // Act - await extensionTwoFactorAuthEmailComponentService.openPopoutIfApprovedForEmail2fa(); - - // Assert - expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({ - title: { key: "warning" }, - content: { key: "popup2faCloseMessage" }, - type: "warning", - }); - - expect(openTwoFactorAuthEmailPopout).not.toHaveBeenCalled(); - }); - - it("should not open a popout if not in the popup", async () => { - // Arrange - configService.getFeatureFlag.mockResolvedValue(false); - jest.spyOn(BrowserPopupUtils, "inPopup").mockReturnValue(false); - - // Act - await extensionTwoFactorAuthEmailComponentService.openPopoutIfApprovedForEmail2fa(); - - // Assert - expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); - expect(openTwoFactorAuthEmailPopout).not.toHaveBeenCalled(); - }); - - it("does not prompt or open a popout if the feature flag is enabled", async () => { - configService.getFeatureFlag.mockResolvedValue(true); - jest.spyOn(BrowserPopupUtils, "inPopup").mockReturnValue(true); - - await extensionTwoFactorAuthEmailComponentService.openPopoutIfApprovedForEmail2fa(); - - expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); - expect(openTwoFactorAuthEmailPopout).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/apps/browser/src/auth/services/extension-two-factor-auth-email-component.service.ts b/apps/browser/src/auth/services/extension-two-factor-auth-email-component.service.ts deleted file mode 100644 index e9cb53f935e..00000000000 --- a/apps/browser/src/auth/services/extension-two-factor-auth-email-component.service.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - DefaultTwoFactorAuthEmailComponentService, - TwoFactorAuthEmailComponentService, -} from "@bitwarden/auth/angular"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { DialogService } from "@bitwarden/components"; - -// FIXME (PM-22628): Popup imports are forbidden in background -// eslint-disable-next-line no-restricted-imports -import { openTwoFactorAuthEmailPopout } from "../../auth/popup/utils/auth-popout-window"; -import BrowserPopupUtils from "../../platform/browser/browser-popup-utils"; - -// TODO: popup state persistence should eventually remove the need for this service -export class ExtensionTwoFactorAuthEmailComponentService - extends DefaultTwoFactorAuthEmailComponentService - implements TwoFactorAuthEmailComponentService -{ - constructor( - private dialogService: DialogService, - private window: Window, - private configService: ConfigService, - ) { - super(); - } - - async openPopoutIfApprovedForEmail2fa(): Promise { - const isTwoFactorFormPersistenceEnabled = await this.configService.getFeatureFlag( - FeatureFlag.PM9115_TwoFactorExtensionDataPersistence, - ); - - if (isTwoFactorFormPersistenceEnabled) { - // If the feature flag is enabled, we don't need to prompt the user to open the popout - return; - } - - if (BrowserPopupUtils.inPopup(this.window)) { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "warning" }, - content: { key: "popup2faCloseMessage" }, - type: "warning", - }); - if (confirmed) { - await openTwoFactorAuthEmailPopout(); - this.window.close(); - } - } - } -} diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index d70418137f8..ca8a76f7bcb 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -24,7 +24,6 @@ import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services. import { LoginComponentService, TwoFactorAuthComponentService, - TwoFactorAuthEmailComponentService, TwoFactorAuthDuoComponentService, TwoFactorAuthWebAuthnComponentService, SsoComponentService, @@ -147,7 +146,6 @@ import { ExtensionSsoComponentService } from "../../auth/popup/login/extension-s import { ExtensionLogoutService } from "../../auth/popup/logout/extension-logout.service"; import { ExtensionTwoFactorAuthComponentService } from "../../auth/services/extension-two-factor-auth-component.service"; import { ExtensionTwoFactorAuthDuoComponentService } from "../../auth/services/extension-two-factor-auth-duo-component.service"; -import { ExtensionTwoFactorAuthEmailComponentService } from "../../auth/services/extension-two-factor-auth-email-component.service"; import { ExtensionTwoFactorAuthWebAuthnComponentService } from "../../auth/services/extension-two-factor-auth-webauthn-component.service"; import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service"; import AutofillService from "../../autofill/services/autofill.service"; @@ -560,11 +558,6 @@ const safeProviders: SafeProvider[] = [ useClass: ExtensionTwoFactorAuthComponentService, deps: [WINDOW], }), - safeProvider({ - provide: TwoFactorAuthEmailComponentService, - useClass: ExtensionTwoFactorAuthEmailComponentService, - deps: [DialogService, WINDOW, ConfigService], - }), safeProvider({ provide: TwoFactorAuthWebAuthnComponentService, useClass: ExtensionTwoFactorAuthWebAuthnComponentService, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index d51d5e650c5..c3f33f2a796 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -24,14 +24,12 @@ import { DefaultRegistrationFinishService, DefaultSetPasswordJitService, DefaultTwoFactorAuthComponentService, - DefaultTwoFactorAuthEmailComponentService, DefaultTwoFactorAuthWebAuthnComponentService, LoginComponentService, LoginDecryptionOptionsService, RegistrationFinishService as RegistrationFinishServiceAbstraction, SetPasswordJitService, TwoFactorAuthComponentService, - TwoFactorAuthEmailComponentService, TwoFactorAuthWebAuthnComponentService, } from "@bitwarden/auth/angular"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. @@ -1471,11 +1469,6 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultTwoFactorAuthWebAuthnComponentService, deps: [], }), - safeProvider({ - provide: TwoFactorAuthEmailComponentService, - useClass: DefaultTwoFactorAuthEmailComponentService, - deps: [], - }), safeProvider({ provide: ViewCacheService, useExisting: NoopViewCacheService, diff --git a/libs/auth/src/angular/two-factor-auth/child-components/index.ts b/libs/auth/src/angular/two-factor-auth/child-components/index.ts index 429da3f14b3..d48cb8a6921 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/index.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/index.ts @@ -1,3 +1,2 @@ -export * from "./two-factor-auth-email"; export * from "./two-factor-auth-duo"; export * from "./two-factor-auth-webauthn"; diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator.component.html b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator/two-factor-auth-authenticator.component.html similarity index 100% rename from libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator.component.html rename to libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator/two-factor-auth-authenticator.component.html diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator/two-factor-auth-authenticator.component.ts similarity index 100% rename from libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator.component.ts rename to libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator/two-factor-auth-authenticator.component.ts diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/default-two-factor-auth-email-component.service.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/default-two-factor-auth-email-component.service.ts deleted file mode 100644 index caae13acc38..00000000000 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/default-two-factor-auth-email-component.service.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TwoFactorAuthEmailComponentService } from "./two-factor-auth-email-component.service"; - -export class DefaultTwoFactorAuthEmailComponentService - implements TwoFactorAuthEmailComponentService { - // no default implementation -} diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/index.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/index.ts deleted file mode 100644 index 91f11b0b7dd..00000000000 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./default-two-factor-auth-email-component.service"; -export * from "./two-factor-auth-email-component.service"; diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-component-email-cache.service.spec.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-component-email-cache.service.spec.ts deleted file mode 100644 index d2d86710b72..00000000000 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-component-email-cache.service.spec.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { TestBed } from "@angular/core/testing"; -import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; - -import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; - -import { - TwoFactorAuthEmailComponentCache, - TwoFactorAuthEmailComponentCacheService, -} from "./two-factor-auth-email-component-cache.service"; - -describe("TwoFactorAuthEmailCache", () => { - describe("fromJSON", () => { - it("returns null when input is null", () => { - const result = TwoFactorAuthEmailComponentCache.fromJSON(null as any); - expect(result).toBeNull(); - }); - - it("creates a TwoFactorAuthEmailCache instance from valid JSON", () => { - const jsonData = { emailSent: true }; - const result = TwoFactorAuthEmailComponentCache.fromJSON(jsonData); - - expect(result).not.toBeNull(); - expect(result).toBeInstanceOf(TwoFactorAuthEmailComponentCache); - expect(result?.emailSent).toBe(true); - }); - }); -}); - -describe("TwoFactorAuthEmailComponentCacheService", () => { - let service: TwoFactorAuthEmailComponentCacheService; - let mockViewCacheService: MockProxy; - let mockConfigService: MockProxy; - let cacheData: BehaviorSubject; - let mockSignal: any; - - beforeEach(() => { - mockViewCacheService = mock(); - mockConfigService = mock(); - cacheData = new BehaviorSubject(null); - mockSignal = jest.fn(() => cacheData.getValue()); - mockSignal.set = jest.fn((value: TwoFactorAuthEmailComponentCache | null) => - cacheData.next(value), - ); - mockViewCacheService.signal.mockReturnValue(mockSignal); - - TestBed.configureTestingModule({ - providers: [ - TwoFactorAuthEmailComponentCacheService, - { provide: ViewCacheService, useValue: mockViewCacheService }, - { provide: ConfigService, useValue: mockConfigService }, - ], - }); - - service = TestBed.inject(TwoFactorAuthEmailComponentCacheService); - }); - - it("creates the service", () => { - expect(service).toBeTruthy(); - }); - - describe("init", () => { - it("sets featureEnabled to true when flag is enabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - - await service.init(); - - expect(mockConfigService.getFeatureFlag).toHaveBeenCalledWith( - FeatureFlag.PM9115_TwoFactorExtensionDataPersistence, - ); - - service.cacheData({ emailSent: true }); - expect(mockSignal.set).toHaveBeenCalled(); - }); - - it("sets featureEnabled to false when flag is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - - await service.init(); - - expect(mockConfigService.getFeatureFlag).toHaveBeenCalledWith( - FeatureFlag.PM9115_TwoFactorExtensionDataPersistence, - ); - - service.cacheData({ emailSent: true }); - expect(mockSignal.set).not.toHaveBeenCalled(); - }); - }); - - describe("cacheData", () => { - beforeEach(async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - await service.init(); - }); - - it("caches email sent state when feature is enabled", () => { - service.cacheData({ emailSent: true }); - - expect(mockSignal.set).toHaveBeenCalledWith({ - emailSent: true, - }); - }); - - it("does not cache data when feature is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - await service.init(); - - service.cacheData({ emailSent: true }); - - expect(mockSignal.set).not.toHaveBeenCalled(); - }); - }); - - describe("clearCachedData", () => { - beforeEach(async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - await service.init(); - }); - - it("clears cached data when feature is enabled", () => { - service.clearCachedData(); - - expect(mockSignal.set).toHaveBeenCalledWith(null); - }); - - it("does not clear cached data when feature is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - await service.init(); - - service.clearCachedData(); - - expect(mockSignal.set).not.toHaveBeenCalled(); - }); - }); - - describe("getCachedData", () => { - beforeEach(async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - await service.init(); - }); - - it("returns cached data when feature is enabled", () => { - const testData = new TwoFactorAuthEmailComponentCache(); - testData.emailSent = true; - cacheData.next(testData); - - const result = service.getCachedData(); - - expect(result).toEqual(testData); - expect(mockSignal).toHaveBeenCalled(); - }); - - it("returns null when feature is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - await service.init(); - - const result = service.getCachedData(); - - expect(result).toBeNull(); - expect(mockSignal).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.spec.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.spec.ts index 36d99ee56ac..e5ab04b51ad 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.spec.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.spec.ts @@ -3,7 +3,6 @@ import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { @@ -61,87 +60,26 @@ describe("TwoFactorAuthEmailComponentCacheService", () => { expect(service).toBeTruthy(); }); - describe("init", () => { - it("sets featureEnabled to true when flag is enabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - - await service.init(); - - expect(mockConfigService.getFeatureFlag).toHaveBeenCalledWith( - FeatureFlag.PM9115_TwoFactorExtensionDataPersistence, - ); - - service.cacheData({ emailSent: true }); - expect(mockSignal.set).toHaveBeenCalled(); - }); - - it("sets featureEnabled to false when flag is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - - await service.init(); - - expect(mockConfigService.getFeatureFlag).toHaveBeenCalledWith( - FeatureFlag.PM9115_TwoFactorExtensionDataPersistence, - ); - - service.cacheData({ emailSent: true }); - expect(mockSignal.set).not.toHaveBeenCalled(); - }); - }); - describe("cacheData", () => { - beforeEach(async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - await service.init(); - }); - - it("caches email sent state when feature is enabled", () => { + it("caches email sent state", () => { service.cacheData({ emailSent: true }); expect(mockSignal.set).toHaveBeenCalledWith({ emailSent: true, }); }); - - it("does not cache data when feature is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - await service.init(); - - service.cacheData({ emailSent: true }); - - expect(mockSignal.set).not.toHaveBeenCalled(); - }); }); describe("clearCachedData", () => { - beforeEach(async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - await service.init(); - }); - - it("clears cached data when feature is enabled", () => { + it("clears cached data", () => { service.clearCachedData(); expect(mockSignal.set).toHaveBeenCalledWith(null); }); - - it("does not clear cached data when feature is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - await service.init(); - - service.clearCachedData(); - - expect(mockSignal.set).not.toHaveBeenCalled(); - }); }); describe("getCachedData", () => { - beforeEach(async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - await service.init(); - }); - - it("returns cached data when feature is enabled", () => { + it("returns cached data", () => { const testData = new TwoFactorAuthEmailComponentCache(); testData.emailSent = true; cacheData.next(testData); @@ -151,15 +89,5 @@ describe("TwoFactorAuthEmailComponentCacheService", () => { expect(result).toEqual(testData); expect(mockSignal).toHaveBeenCalled(); }); - - it("returns null when feature is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - await service.init(); - - const result = service.getCachedData(); - - expect(result).toBeNull(); - expect(mockSignal).not.toHaveBeenCalled(); - }); }); }); diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.ts index d274b8003d7..d98387e1cf5 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component-cache.service.ts @@ -2,8 +2,6 @@ import { inject, Injectable, WritableSignal } from "@angular/core"; import { Jsonify } from "type-fest"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; /** * The key for the email two factor auth component cache. @@ -34,10 +32,6 @@ export class TwoFactorAuthEmailComponentCache { @Injectable() export class TwoFactorAuthEmailComponentCacheService { private viewCacheService: ViewCacheService = inject(ViewCacheService); - private configService: ConfigService = inject(ConfigService); - - /** True when the feature flag is enabled */ - private featureEnabled: boolean = false; /** * Signal for the cached email state. @@ -49,23 +43,10 @@ export class TwoFactorAuthEmailComponentCacheService { deserializer: TwoFactorAuthEmailComponentCache.fromJSON, }); - /** - * Must be called once before interacting with the cached data. - */ - async init() { - this.featureEnabled = await this.configService.getFeatureFlag( - FeatureFlag.PM9115_TwoFactorExtensionDataPersistence, - ); - } - /** * Cache the email sent state. */ cacheData(data: { emailSent: boolean }): void { - if (!this.featureEnabled) { - return; - } - this.emailCache.set({ emailSent: data.emailSent, } as TwoFactorAuthEmailComponentCache); @@ -75,10 +56,6 @@ export class TwoFactorAuthEmailComponentCacheService { * Clear the cached email data. */ clearCachedData(): void { - if (!this.featureEnabled) { - return; - } - this.emailCache.set(null); } @@ -86,10 +63,6 @@ export class TwoFactorAuthEmailComponentCacheService { * Get whether the email has been sent. */ getCachedData(): TwoFactorAuthEmailComponentCache | null { - if (!this.featureEnabled) { - return null; - } - return this.emailCache(); } } diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component.service.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component.service.ts deleted file mode 100644 index fa96b6b96c2..00000000000 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email-component.service.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * A service that manages all cross client functionality for the email 2FA component. - */ -export abstract class TwoFactorAuthEmailComponentService { - /** - * Optionally shows a warning to the user that they might need to popout the - * window to complete email 2FA. - */ - abstract openPopoutIfApprovedForEmail2fa?(): Promise; -} diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email.component.ts index 65641284cf1..9b402f3a956 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email.component.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email.component.ts @@ -25,7 +25,6 @@ import { } from "@bitwarden/components"; import { TwoFactorAuthEmailComponentCacheService } from "./two-factor-auth-email-component-cache.service"; -import { TwoFactorAuthEmailComponentService } from "./two-factor-auth-email-component.service"; @Component({ selector: "app-two-factor-auth-email", @@ -66,14 +65,10 @@ export class TwoFactorAuthEmailComponent implements OnInit { protected apiService: ApiService, protected appIdService: AppIdService, private toastService: ToastService, - private twoFactorAuthEmailComponentService: TwoFactorAuthEmailComponentService, private cacheService: TwoFactorAuthEmailComponentCacheService, ) {} async ngOnInit(): Promise { - await this.twoFactorAuthEmailComponentService.openPopoutIfApprovedForEmail2fa?.(); - await this.cacheService.init(); - // Check if email was already sent const cachedData = this.cacheService.getCachedData(); if (cachedData?.emailSent) { diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey.component.html b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey/two-factor-auth-yubikey.component.html similarity index 100% rename from libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey.component.html rename to libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey/two-factor-auth-yubikey.component.html diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey/two-factor-auth-yubikey.component.ts similarity index 100% rename from libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey.component.ts rename to libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey/two-factor-auth-yubikey.component.ts diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.spec.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.spec.ts index 5b5d486556b..24cd18d43e5 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.spec.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.spec.ts @@ -4,8 +4,6 @@ import { BehaviorSubject } from "rxjs"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { TwoFactorAuthComponentCache, @@ -40,13 +38,11 @@ describe("TwoFactorAuthCache", () => { describe("TwoFactorAuthComponentCacheService", () => { let service: TwoFactorAuthComponentCacheService; let mockViewCacheService: MockProxy; - let mockConfigService: MockProxy; let cacheData: BehaviorSubject; let mockSignal: any; beforeEach(() => { mockViewCacheService = mock(); - mockConfigService = mock(); cacheData = new BehaviorSubject(null); mockSignal = jest.fn(() => cacheData.getValue()); mockSignal.set = jest.fn((value: TwoFactorAuthComponentCache | null) => cacheData.next(value)); @@ -56,7 +52,6 @@ describe("TwoFactorAuthComponentCacheService", () => { providers: [ TwoFactorAuthComponentCacheService, { provide: ViewCacheService, useValue: mockViewCacheService }, - { provide: ConfigService, useValue: mockConfigService }, ], }); @@ -67,41 +62,8 @@ describe("TwoFactorAuthComponentCacheService", () => { expect(service).toBeTruthy(); }); - describe("init", () => { - it("sets featureEnabled to true when flag is enabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - - await service.init(); - - expect(mockConfigService.getFeatureFlag).toHaveBeenCalledWith( - FeatureFlag.PM9115_TwoFactorExtensionDataPersistence, - ); - - service.cacheData({ token: "123456" }); - expect(mockSignal.set).toHaveBeenCalled(); - }); - - it("sets featureEnabled to false when flag is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - - await service.init(); - - expect(mockConfigService.getFeatureFlag).toHaveBeenCalledWith( - FeatureFlag.PM9115_TwoFactorExtensionDataPersistence, - ); - - service.cacheData({ token: "123456" }); - expect(mockSignal.set).not.toHaveBeenCalled(); - }); - }); - describe("cacheData", () => { - beforeEach(async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - await service.init(); - }); - - it("caches complete data when feature is enabled", () => { + it("caches complete data", () => { const testData: TwoFactorAuthComponentData = { token: "123456", remember: true, @@ -117,7 +79,7 @@ describe("TwoFactorAuthComponentCacheService", () => { }); }); - it("caches partial data when feature is enabled", () => { + it("caches partial data", () => { service.cacheData({ token: "123456" }); expect(mockSignal.set).toHaveBeenCalledWith({ @@ -126,46 +88,18 @@ describe("TwoFactorAuthComponentCacheService", () => { selectedProviderType: undefined, }); }); - - it("does not cache data when feature is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - await service.init(); - - service.cacheData({ token: "123456" }); - - expect(mockSignal.set).not.toHaveBeenCalled(); - }); }); describe("clearCachedData", () => { - beforeEach(async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - await service.init(); - }); - - it("clears cached data when feature is enabled", () => { + it("clears cached data", () => { service.clearCachedData(); expect(mockSignal.set).toHaveBeenCalledWith(null); }); - - it("does not clear cached data when feature is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - await service.init(); - - service.clearCachedData(); - - expect(mockSignal.set).not.toHaveBeenCalled(); - }); }); describe("getCachedData", () => { - beforeEach(async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - await service.init(); - }); - - it("returns cached data when feature is enabled", () => { + it("returns cached data", () => { const testData = new TwoFactorAuthComponentCache(); testData.token = "123456"; testData.remember = true; @@ -177,15 +111,5 @@ describe("TwoFactorAuthComponentCacheService", () => { expect(result).toEqual(testData); expect(mockSignal).toHaveBeenCalled(); }); - - it("returns null when feature is disabled", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(false); - await service.init(); - - const result = service.getCachedData(); - - expect(result).toBeNull(); - expect(mockSignal).not.toHaveBeenCalled(); - }); }); }); diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.ts index 2d9fcaa5633..33aa76680e4 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component-cache.service.ts @@ -3,8 +3,6 @@ import { Jsonify } from "type-fest"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; const TWO_FACTOR_AUTH_COMPONENT_CACHE_KEY = "two-factor-auth-component-cache"; @@ -40,10 +38,6 @@ export interface TwoFactorAuthComponentData { @Injectable() export class TwoFactorAuthComponentCacheService { private viewCacheService: ViewCacheService = inject(ViewCacheService); - private configService: ConfigService = inject(ConfigService); - - /** True when the `PM9115_TwoFactorExtensionDataPersistence` flag is enabled */ - private featureEnabled: boolean = false; /** * Signal for the cached TwoFactorAuthData. @@ -57,23 +51,10 @@ export class TwoFactorAuthComponentCacheService { constructor() {} - /** - * Must be called once before interacting with the cached data. - */ - async init() { - this.featureEnabled = await this.configService.getFeatureFlag( - FeatureFlag.PM9115_TwoFactorExtensionDataPersistence, - ); - } - /** * Update the cache with the new TwoFactorAuthData. */ cacheData(data: TwoFactorAuthComponentData): void { - if (!this.featureEnabled) { - return; - } - this.twoFactorAuthComponentCache.set({ token: data.token, remember: data.remember, @@ -85,10 +66,6 @@ export class TwoFactorAuthComponentCacheService { * Clears the cached TwoFactorAuthData. */ clearCachedData(): void { - if (!this.featureEnabled) { - return; - } - this.twoFactorAuthComponentCache.set(null); } @@ -96,10 +73,6 @@ export class TwoFactorAuthComponentCacheService { * Returns the cached TwoFactorAuthData (when available). */ getCachedData(): TwoFactorAuthComponentCache | null { - if (!this.featureEnabled) { - return null; - } - return this.twoFactorAuthComponentCache(); } } diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts index 4ab3841e48e..e7678102360 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts @@ -121,7 +121,6 @@ describe("TwoFactorAuthComponent", () => { mockTwoFactorAuthCompCacheService = mock(); mockTwoFactorAuthCompCacheService.getCachedData.mockReturnValue(null); - mockTwoFactorAuthCompCacheService.init.mockResolvedValue(); mockUserDecryptionOpts = { noMasterPassword: new UserDecryptionOptions({ diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts index a281411f971..50cc2d88d6a 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts @@ -60,11 +60,11 @@ import { TwoFactorAuthDuoIcon, } from "../icons/two-factor-auth"; -import { TwoFactorAuthAuthenticatorComponent } from "./child-components/two-factor-auth-authenticator.component"; +import { TwoFactorAuthAuthenticatorComponent } from "./child-components/two-factor-auth-authenticator/two-factor-auth-authenticator.component"; import { TwoFactorAuthDuoComponent } from "./child-components/two-factor-auth-duo/two-factor-auth-duo.component"; import { TwoFactorAuthEmailComponent } from "./child-components/two-factor-auth-email/two-factor-auth-email.component"; import { TwoFactorAuthWebAuthnComponent } from "./child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component"; -import { TwoFactorAuthYubikeyComponent } from "./child-components/two-factor-auth-yubikey.component"; +import { TwoFactorAuthYubikeyComponent } from "./child-components/two-factor-auth-yubikey/two-factor-auth-yubikey.component"; import { TwoFactorAuthComponentCacheService, TwoFactorAuthComponentData, @@ -180,9 +180,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy { this.listenForAuthnSessionTimeout(); - // Initialize the cache - await this.twoFactorAuthComponentCacheService.init(); - // Load cached form data if available let loadedCachedProviderType = false; const cachedData = this.twoFactorAuthComponentCacheService.getCachedData(); diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 8d9eebe6f9f..71de8fb5433 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -17,7 +17,6 @@ export enum FeatureFlag { /* Auth */ PM16117_SetInitialPasswordRefactor = "pm-16117-set-initial-password-refactor", PM16117_ChangeExistingPasswordRefactor = "pm-16117-change-existing-password-refactor", - PM9115_TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence", PM14938_BrowserExtensionLoginApproval = "pm-14938-browser-extension-login-approvals", /* Autofill */ @@ -107,7 +106,6 @@ export const DefaultFeatureFlagValue = { /* Auth */ [FeatureFlag.PM16117_SetInitialPasswordRefactor]: FALSE, [FeatureFlag.PM16117_ChangeExistingPasswordRefactor]: FALSE, - [FeatureFlag.PM9115_TwoFactorExtensionDataPersistence]: FALSE, [FeatureFlag.PM14938_BrowserExtensionLoginApproval]: FALSE, /* Billing */