diff --git a/apps/web/src/app/auth/recover-two-factor.component.spec.ts b/apps/web/src/app/auth/recover-two-factor.component.spec.ts index 40182dee017..bf6d47e09e5 100644 --- a/apps/web/src/app/auth/recover-two-factor.component.spec.ts +++ b/apps/web/src/app/auth/recover-two-factor.component.spec.ts @@ -9,7 +9,6 @@ import { } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; -import { TwoFactorRecoveryRequest } from "@bitwarden/common/auth/models/request/two-factor-recovery.request"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -85,15 +84,14 @@ describe("RecoverTwoFactorComponent", () => { describe("handleRecoveryLogin", () => { it("should log in successfully and navigate to the two-factor settings page", async () => { // Arrange - const request = new TwoFactorRecoveryRequest(); - request.recoveryCode = "testRecoveryCode"; - request.email = "test@example.com"; + const email = "test@example.com"; + const recoveryCode = "testRecoveryCode"; const authResult = new AuthResult(); mockLoginStrategyService.logIn.mockResolvedValue(authResult); // Act - await component["handleRecoveryLogin"](request); + await component["loginWithRecoveryCode"](email, recoveryCode); // Assert expect(mockLoginStrategyService.logIn).toHaveBeenCalledWith( @@ -112,15 +110,14 @@ describe("RecoverTwoFactorComponent", () => { it("should handle login errors and redirect to login page", async () => { // Arrange - const request = new TwoFactorRecoveryRequest(); - request.recoveryCode = "testRecoveryCode"; - request.email = "test@example.com"; + const email = "test@example.com"; + const recoveryCode = "testRecoveryCode"; const error = new Error("Login failed"); mockLoginStrategyService.logIn.mockRejectedValue(error); // Act - await component["handleRecoveryLogin"](request); + await component["loginWithRecoveryCode"](email, recoveryCode); // Assert expect(mockLogService.error).toHaveBeenCalledWith( @@ -128,7 +125,7 @@ describe("RecoverTwoFactorComponent", () => { error.message, ); expect(mockRouter.navigate).toHaveBeenCalledWith(["/login"], { - queryParams: { email: request.email }, + queryParams: { email: email }, }); }); }); diff --git a/apps/web/src/app/auth/recover-two-factor.component.ts b/apps/web/src/app/auth/recover-two-factor.component.ts index 996b324ce56..35aa1aab7c1 100644 --- a/apps/web/src/app/auth/recover-two-factor.component.ts +++ b/apps/web/src/app/auth/recover-two-factor.component.ts @@ -7,17 +7,11 @@ import { PasswordLoginCredentials, LoginSuccessHandlerService, } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; -import { TwoFactorRecoveryRequest } from "@bitwarden/common/auth/models/request/two-factor-recovery.request"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; 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"; import { ToastService } from "@bitwarden/components"; -import { KeyService } from "@bitwarden/key-management"; import { NewDeviceVerificationNoticeService } from "@bitwarden/vault"; @Component({ @@ -36,32 +30,18 @@ export class RecoverTwoFactorComponent implements OnInit { */ recoveryCodeMessage = ""; - /** - * Whether the recovery code login feature flag is enabled - */ - private recoveryCodeLoginFeatureFlagEnabled = false; - constructor( private router: Router, - private apiService: ApiService, - private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, - private keyService: KeyService, private loginStrategyService: LoginStrategyServiceAbstraction, private toastService: ToastService, - private configService: ConfigService, private loginSuccessHandlerService: LoginSuccessHandlerService, private logService: LogService, private newDeviceVerificationNoticeService: NewDeviceVerificationNoticeService, ) {} async ngOnInit() { - this.recoveryCodeLoginFeatureFlagEnabled = await this.configService.getFeatureFlag( - FeatureFlag.RecoveryCodeLogin, - ); - this.recoveryCodeMessage = this.recoveryCodeLoginFeatureFlagEnabled - ? this.i18nService.t("logInBelowUsingYourSingleUseRecoveryCode") - : this.i18nService.t("recoverAccountTwoStepDesc"); + this.recoveryCodeMessage = this.i18nService.t("logInBelowUsingYourSingleUseRecoveryCode"); } get email(): string { @@ -85,38 +65,25 @@ export class RecoverTwoFactorComponent implements OnInit { return; } - const request = new TwoFactorRecoveryRequest(); - request.recoveryCode = this.recoveryCode.replace(/\s/g, "").toLowerCase(); - request.email = this.email.trim().toLowerCase(); - const key = await this.loginStrategyService.makePreloginKey(this.masterPassword, request.email); - request.masterPasswordHash = await this.keyService.hashMasterKey(this.masterPassword, key); + const email = this.email.trim().toLowerCase(); + const recoveryCode = this.recoveryCode.replace(/\s/g, "").toLowerCase(); - if (this.recoveryCodeLoginFeatureFlagEnabled) { - await this.handleRecoveryLogin(request); - } else { - await this.apiService.postTwoFactorRecover(request); - this.toastService.showToast({ - variant: "success", - title: "", - message: this.i18nService.t("twoStepRecoverDisabled"), - }); - await this.router.navigate(["/"]); - } + await this.loginWithRecoveryCode(email, recoveryCode); }; /** * Handles the login process after a successful account recovery. */ - private async handleRecoveryLogin(request: TwoFactorRecoveryRequest) { + private async loginWithRecoveryCode(email: string, recoveryCode: string) { // Build two-factor request to pass into PasswordLoginCredentials request using the 2FA recovery code and RecoveryCode type const twoFactorRequest: TokenTwoFactorRequest = { provider: TwoFactorProviderType.RecoveryCode, - token: request.recoveryCode, + token: recoveryCode, remember: false, }; const credentials = new PasswordLoginCredentials( - request.email, + email, this.masterPassword, "", twoFactorRequest, @@ -148,7 +115,7 @@ export class RecoverTwoFactorComponent implements OnInit { } catch (error) { // If login errors, redirect to login page per product. Don't show error this.logService.error("Error logging in automatically: ", (error as Error).message); - await this.router.navigate(["/login"], { queryParams: { email: request.email } }); + await this.router.navigate(["/login"], { queryParams: { email: email } }); } } } diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.ts index a76505930d4..83c9ff23f3c 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.ts @@ -29,7 +29,6 @@ import { TwoFactorProviders } from "@bitwarden/common/auth/services/two-factor.s import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -85,12 +84,7 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { } async ngOnInit() { - const recoveryCodeLoginFeatureFlagEnabled = await this.configService.getFeatureFlag( - FeatureFlag.RecoveryCodeLogin, - ); - this.recoveryCodeWarningMessage = recoveryCodeLoginFeatureFlagEnabled - ? this.i18nService.t("yourSingleUseRecoveryCode") - : this.i18nService.t("twoStepLoginRecoveryWarning"); + this.recoveryCodeWarningMessage = this.i18nService.t("yourSingleUseRecoveryCode"); for (const key in TwoFactorProviders) { // eslint-disable-next-line diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index cfef1faa7ba..f186787f7c0 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -51,7 +51,6 @@ import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-aut import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request"; import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request"; import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request"; -import { TwoFactorRecoveryRequest } from "../auth/models/request/two-factor-recovery.request"; import { UpdateProfileRequest } from "../auth/models/request/update-profile.request"; import { UpdateTwoFactorAuthenticatorRequest } from "../auth/models/request/update-two-factor-authenticator.request"; import { UpdateTwoFactorDuoRequest } from "../auth/models/request/update-two-factor-duo.request"; @@ -344,7 +343,6 @@ export abstract class ApiService { organizationId: string, request: TwoFactorProviderRequest, ) => Promise; - postTwoFactorRecover: (request: TwoFactorRecoveryRequest) => Promise; postTwoFactorEmailSetup: (request: TwoFactorEmailRequest) => Promise; postTwoFactorEmail: (request: TwoFactorEmailRequest) => Promise; getDeviceVerificationSettings: () => Promise; diff --git a/libs/common/src/auth/models/request/two-factor-recovery.request.ts b/libs/common/src/auth/models/request/two-factor-recovery.request.ts deleted file mode 100644 index 79ef6da280c..00000000000 --- a/libs/common/src/auth/models/request/two-factor-recovery.request.ts +++ /dev/null @@ -1,8 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { SecretVerificationRequest } from "./secret-verification.request"; - -export class TwoFactorRecoveryRequest extends SecretVerificationRequest { - recoveryCode: string; - email: string; -} diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index c6f9c7daceb..dda27d202de 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -44,7 +44,6 @@ export enum FeatureFlag { ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs", AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner", PM15179_AddExistingOrgsFromProviderPortal = "pm-15179-add-existing-orgs-from-provider-portal", - RecoveryCodeLogin = "pm-17128-recovery-code-login", PM12276_BreadcrumbEventLogs = "pm-12276-breadcrumbing-for-business-features", PM18794_ProviderPaymentMethod = "pm-18794-provider-payment-method", } @@ -101,7 +100,6 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.ResellerManagedOrgAlert]: FALSE, [FeatureFlag.AccountDeprovisioningBanner]: FALSE, [FeatureFlag.PM15179_AddExistingOrgsFromProviderPortal]: FALSE, - [FeatureFlag.RecoveryCodeLogin]: FALSE, [FeatureFlag.PM12276_BreadcrumbEventLogs]: FALSE, [FeatureFlag.PM18794_ProviderPaymentMethod]: FALSE, } satisfies Record; diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index db4e5fdbc8f..2ff2fb01c87 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -59,7 +59,6 @@ import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-aut import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request"; import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request"; import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request"; -import { TwoFactorRecoveryRequest } from "../auth/models/request/two-factor-recovery.request"; import { UpdateProfileRequest } from "../auth/models/request/update-profile.request"; import { UpdateTwoFactorAuthenticatorRequest } from "../auth/models/request/update-two-factor-authenticator.request"; import { UpdateTwoFactorDuoRequest } from "../auth/models/request/update-two-factor-duo.request"; @@ -1064,10 +1063,6 @@ export class ApiService implements ApiServiceAbstraction { return new TwoFactorProviderResponse(r); } - postTwoFactorRecover(request: TwoFactorRecoveryRequest): Promise { - return this.send("POST", "/two-factor/recover", request, false, false); - } - postTwoFactorEmailSetup(request: TwoFactorEmailRequest): Promise { return this.send("POST", "/two-factor/send-email", request, true, false); }