From 1b441e8a0f3460ce78618a86c840c96be1c01c2e Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 25 Jun 2025 07:25:41 -0700 Subject: [PATCH] fix(routing): [PM-22995] update routing and tests (#15320) Updates routing in 2 components to account for feature flag: `PM16117_SetInitialPasswordRefactor` --- libs/auth/src/angular/sso/sso.component.ts | 10 +- .../two-factor-auth.component.spec.ts | 96 ++++++++++++++----- .../two-factor-auth.component.ts | 10 +- 3 files changed, 88 insertions(+), 28 deletions(-) diff --git a/libs/auth/src/angular/sso/sso.component.ts b/libs/auth/src/angular/sso/sso.component.ts index b78ca098dea..07b59ac661f 100644 --- a/libs/auth/src/angular/sso/sso.component.ts +++ b/libs/auth/src/angular/sso/sso.component.ts @@ -23,10 +23,12 @@ import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { SsoPreValidateResponse } from "@bitwarden/common/auth/models/response/sso-pre-validate.response"; import { ClientType, HttpStatusCode } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -116,6 +118,7 @@ export class SsoComponent implements OnInit { private toastService: ToastService, private ssoComponentService: SsoComponentService, private loginSuccessHandlerService: LoginSuccessHandlerService, + private configService: ConfigService, ) { environmentService.environment$.pipe(takeUntilDestroyed()).subscribe((env) => { this.redirectUri = env.getWebVaultUrl() + "/sso-connector.html"; @@ -531,7 +534,12 @@ export class SsoComponent implements OnInit { } private async handleChangePasswordRequired(orgIdentifier: string) { - await this.router.navigate(["set-password-jit"], { + const isSetInitialPasswordRefactorFlagOn = await this.configService.getFeatureFlag( + FeatureFlag.PM16117_SetInitialPasswordRefactor, + ); + const route = isSetInitialPasswordRefactorFlagOn ? "set-initial-password" : "set-password-jit"; + + await this.router.navigate([route], { queryParams: { identifier: orgIdentifier, }, 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 00cad105f95..4ab3841e48e 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 @@ -27,6 +27,7 @@ import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/ide import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -75,6 +76,7 @@ describe("TwoFactorAuthComponent", () => { let mockLoginSuccessHandlerService: MockProxy; let mockTwoFactorAuthCompCacheService: MockProxy; let mockAuthService: MockProxy; + let mockConfigService: MockProxy; let mockUserDecryptionOpts: { noMasterPassword: UserDecryptionOptions; @@ -110,6 +112,7 @@ describe("TwoFactorAuthComponent", () => { mockToastService = mock(); mockTwoFactorAuthCompService = mock(); mockAuthService = mock(); + mockConfigService = mock(); mockEnvService = mock(); mockLoginSuccessHandlerService = mock(); @@ -209,6 +212,7 @@ describe("TwoFactorAuthComponent", () => { useValue: mockTwoFactorAuthCompCacheService, }, { provide: AuthService, useValue: mockAuthService }, + { provide: ConfigService, useValue: mockConfigService }, ], }); @@ -225,22 +229,6 @@ describe("TwoFactorAuthComponent", () => { expect(component).toBeTruthy(); }); - // Shared tests - const testChangePasswordOnSuccessfulLogin = () => { - it("navigates to the component's defined change password route when user doesn't have a MP and key connector isn't enabled", async () => { - // Act - await component.submit("testToken"); - - // Assert - expect(mockRouter.navigate).toHaveBeenCalledTimes(1); - expect(mockRouter.navigate).toHaveBeenCalledWith(["set-password"], { - queryParams: { - identifier: component.orgSsoIdentifier, - }, - }); - }); - }; - describe("Standard 2FA scenarios", () => { describe("submit", () => { const token = "testToken"; @@ -280,20 +268,76 @@ describe("TwoFactorAuthComponent", () => { selectedUserDecryptionOptions.next(mockUserDecryptionOpts.noMasterPassword); }); - testChangePasswordOnSuccessfulLogin(); + describe("Given the PM16117_SetInitialPasswordRefactor feature flag is ON", () => { + it("navigates to the /set-initial-password route when user doesn't have a MP and key connector isn't enabled", async () => { + // Arrange + mockConfigService.getFeatureFlag.mockResolvedValue(true); + + // Act + await component.submit("testToken"); + + // Assert + expect(mockRouter.navigate).toHaveBeenCalledTimes(1); + expect(mockRouter.navigate).toHaveBeenCalledWith(["set-initial-password"], { + queryParams: { + identifier: component.orgSsoIdentifier, + }, + }); + }); + }); + + describe("Given the PM16117_SetInitialPasswordRefactor feature flag is OFF", () => { + it("navigates to the /set-password route when user doesn't have a MP and key connector isn't enabled", async () => { + // Arrange + mockConfigService.getFeatureFlag.mockResolvedValue(false); + + // Act + await component.submit("testToken"); + + // Assert + expect(mockRouter.navigate).toHaveBeenCalledTimes(1); + expect(mockRouter.navigate).toHaveBeenCalledWith(["set-password"], { + queryParams: { + identifier: component.orgSsoIdentifier, + }, + }); + }); + }); }); - it("does not navigate to the change password route when the user has key connector even if user has no master password", async () => { - selectedUserDecryptionOptions.next( - mockUserDecryptionOpts.noMasterPasswordWithKeyConnector, - ); + describe("Given the PM16117_SetInitialPasswordRefactor feature flag is ON", () => { + it("does not navigate to the /set-initial-password route when the user has key connector even if user has no master password", async () => { + mockConfigService.getFeatureFlag.mockResolvedValue(true); - await component.submit(token, remember); + selectedUserDecryptionOptions.next( + mockUserDecryptionOpts.noMasterPasswordWithKeyConnector, + ); - expect(mockRouter.navigate).not.toHaveBeenCalledWith(["set-password"], { - queryParams: { - identifier: component.orgSsoIdentifier, - }, + await component.submit(token, remember); + + expect(mockRouter.navigate).not.toHaveBeenCalledWith(["set-initial-password"], { + queryParams: { + identifier: component.orgSsoIdentifier, + }, + }); + }); + }); + + describe("Given the PM16117_SetInitialPasswordRefactor feature flag is OFF", () => { + it("does not navigate to the /set-password route when the user has key connector even if user has no master password", async () => { + mockConfigService.getFeatureFlag.mockResolvedValue(false); + + selectedUserDecryptionOptions.next( + mockUserDecryptionOpts.noMasterPasswordWithKeyConnector, + ); + + await component.submit(token, remember); + + expect(mockRouter.navigate).not.toHaveBeenCalledWith(["set-password"], { + queryParams: { + identifier: component.orgSsoIdentifier, + }, + }); }); }); }); 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 b811d48a48f..43a63498634 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 @@ -32,7 +32,9 @@ import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-p import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -169,6 +171,7 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy { private loginSuccessHandlerService: LoginSuccessHandlerService, private twoFactorAuthComponentCacheService: TwoFactorAuthComponentCacheService, private authService: AuthService, + private configService: ConfigService, ) {} async ngOnInit() { @@ -559,7 +562,12 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy { } private async handleChangePasswordRequired(orgIdentifier: string | undefined) { - await this.router.navigate(["set-password"], { + const isSetInitialPasswordRefactorFlagOn = await this.configService.getFeatureFlag( + FeatureFlag.PM16117_SetInitialPasswordRefactor, + ); + const route = isSetInitialPasswordRefactorFlagOn ? "set-initial-password" : "set-password"; + + await this.router.navigate([route], { queryParams: { identifier: orgIdentifier, },