From 1101f35b65c1046fc1261ac46c0d20ba38be2ed6 Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Wed, 29 Oct 2025 15:19:26 -0400 Subject: [PATCH 01/11] refactor(two-factor-service) [PM-21204]: Stub API methods in TwoFactorService (domain). --- .../src/services/jslib-services.module.ts | 7 +- .../auth/abstractions/two-factor.service.ts | 269 ++++++++++++++++++ .../src/auth/services/two-factor.service.ts | 93 ++++++ 3 files changed, 368 insertions(+), 1 deletion(-) diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 94b9f6240a49..291db0886fdc 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1164,7 +1164,12 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: TwoFactorServiceAbstraction, useClass: TwoFactorService, - deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction, GlobalStateProvider], + deps: [ + I18nServiceAbstraction, + PlatformUtilsServiceAbstraction, + GlobalStateProvider, + TwoFactorApiService, + ], }), safeProvider({ provide: FormValidationErrorsServiceAbstraction, diff --git a/libs/common/src/auth/abstractions/two-factor.service.ts b/libs/common/src/auth/abstractions/two-factor.service.ts index 528e52bf5dac..b56c63479e53 100644 --- a/libs/common/src/auth/abstractions/two-factor.service.ts +++ b/libs/common/src/auth/abstractions/two-factor.service.ts @@ -1,5 +1,17 @@ +import { ListResponse } from "../../models/response/list.response"; import { TwoFactorProviderType } from "../enums/two-factor-provider-type"; import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response"; +import { TwoFactorAuthenticatorResponse } from "../models/response/two-factor-authenticator.response"; +import { TwoFactorDuoResponse } from "../models/response/two-factor-duo.response"; +import { TwoFactorEmailResponse } from "../models/response/two-factor-email.response"; +import { TwoFactorProviderResponse } from "../models/response/two-factor-provider.response"; +import { TwoFactorRecoverResponse } from "../models/response/two-factor-recover.response"; +import { + ChallengeResponse, + TwoFactorWebAuthnResponse, +} from "../models/response/two-factor-web-authn.response"; +import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key.response"; +import { Verification } from "../types/verification"; export interface TwoFactorProviderDetails { type: TwoFactorProviderType; @@ -57,4 +69,261 @@ export abstract class TwoFactorService { * @returns A list of two-factor providers or null if none are stored in state. */ abstract getProviders(): Promise | null>; + + /** + * Gets the enabled two-factor providers for the current user from the API. + * Used for settings management. + * @returns A promise that resolves to a list response containing enabled two-factor provider configurations. + */ + abstract getEnabledTwoFactorProviders(): Promise>; + + /** + * Gets the enabled two-factor providers for an organization from the API. + * Requires organization administrator permissions. + * Used for settings management. + * + * @param organizationId The ID of the organization. + * @returns A promise that resolves to a list response containing enabled two-factor provider configurations. + */ + abstract getTwoFactorOrganizationProviders( + organizationId: string, + ): Promise>; + + /** + * Gets the authenticator (TOTP) two-factor configuration for the current user from the API. + * Requires user verification via master password or OTP. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the authenticator configuration including the secret key. + */ + abstract getTwoFactorAuthenticator( + verification: Verification, + ): Promise; + + /** + * Gets the email two-factor configuration for the current user from the API. + * Requires user verification via master password or OTP. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the email two-factor configuration. + */ + abstract getTwoFactorEmail(verification: Verification): Promise; + + /** + * Gets the Duo two-factor configuration for the current user from the API. + * Requires user verification and an active premium subscription. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the Duo configuration. + */ + abstract getTwoFactorDuo(verification: Verification): Promise; + + /** + * Gets the Duo two-factor configuration for an organization from the API. + * Requires user verification and organization policy management permissions. + * Used for settings management. + * + * @param organizationId The ID of the organization. + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the organization Duo configuration. + */ + abstract getTwoFactorOrganizationDuo( + organizationId: string, + verification: Verification, + ): Promise; + + /** + * Gets the YubiKey OTP two-factor configuration for the current user from the API. + * Requires user verification and an active premium subscription. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the YubiKey configuration. + */ + abstract getTwoFactorYubiKey(verification: Verification): Promise; + + /** + * Gets the WebAuthn (FIDO2) two-factor configuration for the current user from the API. + * Requires user verification via master password or OTP. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the WebAuthn configuration including registered credentials. + */ + abstract getTwoFactorWebAuthn(verification: Verification): Promise; + + /** + * Gets a WebAuthn challenge for registering a new WebAuthn credential from the API. + * This must be called before putTwoFactorWebAuthn to obtain the cryptographic challenge + * required for credential creation. The challenge is used by the browser's WebAuthn API. + * Requires user verification via master password or OTP. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the credential creation options containing the challenge. + */ + abstract getTwoFactorWebAuthnChallenge(verification: Verification): Promise; + + /** + * Gets the recovery code configuration for the current user from the API. + * The recovery code should be stored securely by the user. + * Requires user verification via master password or OTP. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the recovery code configuration. + */ + abstract getTwoFactorRecover(verification: Verification): Promise; + + /** + * Enables or updates the authenticator (TOTP) two-factor provider. + * Validates the provided token against the shared secret before enabling. + * The token must be generated by an authenticator app using the secret key. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated authenticator configuration. + */ + abstract putTwoFactorAuthenticator( + verification: Verification, + ): Promise; + + /** + * Disables the authenticator (TOTP) two-factor provider for the current user. + * Requires user verification token to confirm the operation. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated provider status. + */ + abstract deleteTwoFactorAuthenticator( + verification: Verification, + ): Promise; + + /** + * Enables or updates the email two-factor provider for the current user. + * Validates the email verification token sent via postTwoFactorEmailSetup before enabling. + * The token must match the code sent to the specified email address. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated email two-factor configuration. + */ + abstract putTwoFactorEmail(verification: Verification): Promise; + + /** + * Enables or updates the Duo two-factor provider for the current user. + * Validates the Duo configuration (client ID, client secret, and host) before enabling. + * Requires user verification and an active premium subscription. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated Duo configuration. + */ + abstract putTwoFactorDuo(verification: Verification): Promise; + + /** + * Enables or updates the Duo two-factor provider for an organization. + * Validates the Duo configuration (client ID, client secret, and host) before enabling. + * Requires user verification and organization policy management permissions. + * Used for settings management. + * + * @param organizationId The ID of the organization. + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated organization Duo configuration. + */ + abstract putTwoFactorOrganizationDuo( + organizationId: string, + verification: Verification, + ): Promise; + + /** + * Enables or updates the YubiKey OTP two-factor provider for the current user. + * Validates each provided YubiKey by testing an OTP from the device. + * Supports up to 5 YubiKey devices. Empty key slots are allowed. + * Requires user verification and an active premium subscription. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated YubiKey configuration. + */ + abstract putTwoFactorYubiKey(verification: Verification): Promise; + + /** + * Registers a new WebAuthn (FIDO2) credential for two-factor authentication for the current user. + * Must be called after getTwoFactorWebAuthnChallenge to complete the registration flow. + * The device response contains the signed challenge from the authenticator device. + * Requires user verification via master password or OTP. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated WebAuthn configuration with the new credential. + */ + abstract putTwoFactorWebAuthn(verification: Verification): Promise; + + /** + * Removes a specific WebAuthn (FIDO2) credential from the user's account. + * The credential will no longer be usable for two-factor authentication. + * Other registered WebAuthn credentials remain active. + * Requires user verification via master password or OTP. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated WebAuthn configuration. + */ + abstract deleteTwoFactorWebAuthn(verification: Verification): Promise; + + /** + * Disables a specific two-factor provider for the current user. + * The provider will no longer be required or usable for authentication. + * Requires user verification via master password or OTP. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated provider status. + */ + abstract putTwoFactorDisable(verification: Verification): Promise; + + /** + * Disables a specific two-factor provider for an organization. + * The provider will no longer be available for organization members. + * Requires user verification and organization policy management permissions. + * Used for settings management. + * + * @param organizationId The ID of the organization. + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves to the updated provider status. + */ + abstract putTwoFactorOrganizationDisable( + organizationId: string, + verification: Verification, + ): Promise; + + /** + * Initiates email two-factor setup by sending a verification code to the specified email address. + * This is the first step in enabling email two-factor authentication. + * The verification code must be provided to putTwoFactorEmail to complete setup. + * Only used during initial configuration, not during login flows. + * Requires user verification via master password or OTP. + * Used for settings management. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves when the verification email has been sent. + */ + abstract postTwoFactorEmailSetup(verification: Verification): Promise; + + /** + * Sends a two-factor authentication code via email during the login flow. + * Supports multiple authentication contexts including standard login, SSO, and passwordless flows. + * This is used to deliver codes during authentication, not during initial setup. + * May be called without authentication for login scenarios. + * Used during authentication flows. + * + * @param verification The verification information to authenticate the user. + * @returns A promise that resolves when the authentication email has been sent. + */ + abstract postTwoFactorEmail(verification: Verification): Promise; } diff --git a/libs/common/src/auth/services/two-factor.service.ts b/libs/common/src/auth/services/two-factor.service.ts index 83e113268a21..a45f840f7f4b 100644 --- a/libs/common/src/auth/services/two-factor.service.ts +++ b/libs/common/src/auth/services/two-factor.service.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { firstValueFrom, map } from "rxjs"; +import { ListResponse } from "../../models/response/list.response"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service"; import { Utils } from "../../platform/misc/utils"; @@ -12,6 +13,18 @@ import { } from "../abstractions/two-factor.service"; import { TwoFactorProviderType } from "../enums/two-factor-provider-type"; import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response"; +import { TwoFactorAuthenticatorResponse } from "../models/response/two-factor-authenticator.response"; +import { TwoFactorDuoResponse } from "../models/response/two-factor-duo.response"; +import { TwoFactorEmailResponse } from "../models/response/two-factor-email.response"; +import { TwoFactorProviderResponse } from "../models/response/two-factor-provider.response"; +import { TwoFactorRecoverResponse } from "../models/response/two-factor-recover.response"; +import { + TwoFactorWebAuthnResponse, + ChallengeResponse, +} from "../models/response/two-factor-web-authn.response"; +import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key.response"; +import { TwoFactorApiService } from "../two-factor"; +import { Verification } from "../types/verification"; export const TwoFactorProviders: Partial> = { @@ -95,6 +108,7 @@ export class TwoFactorService implements TwoFactorServiceAbstraction { private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private globalStateProvider: GlobalStateProvider, + private twoFactorApiService: TwoFactorApiService, ) {} init() { @@ -209,4 +223,83 @@ export class TwoFactorService implements TwoFactorServiceAbstraction { getProviders(): Promise | null> { return firstValueFrom(this.providers$); } + + async getEnabledTwoFactorProviders(): Promise> { + return this.twoFactorApiService.getTwoFactorProviders(); + } + + getTwoFactorOrganizationProviders( + organizationId: string, + ): Promise> { + throw new Error("Method not implemented."); + } + getTwoFactorAuthenticator(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + getTwoFactorEmail(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + getTwoFactorDuo(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + getTwoFactorOrganizationDuo( + organizationId: string, + verification: Verification, + ): Promise { + throw new Error("Method not implemented."); + } + getTwoFactorYubiKey(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + getTwoFactorWebAuthn(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + getTwoFactorWebAuthnChallenge(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + getTwoFactorRecover(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + putTwoFactorAuthenticator(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + deleteTwoFactorAuthenticator(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + putTwoFactorEmail(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + putTwoFactorDuo(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + putTwoFactorOrganizationDuo( + organizationId: string, + verification: Verification, + ): Promise { + throw new Error("Method not implemented."); + } + putTwoFactorYubiKey(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + putTwoFactorWebAuthn(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + deleteTwoFactorWebAuthn(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + putTwoFactorDisable(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + putTwoFactorOrganizationDisable( + organizationId: string, + verification: Verification, + ): Promise { + throw new Error("Method not implemented."); + } + postTwoFactorEmailSetup(verification: Verification): Promise { + throw new Error("Method not implemented."); + } + postTwoFactorEmail(verification: Verification): Promise { + throw new Error("Method not implemented."); + } } From 6b419765a17fffbf3e37b5c4a8a09b13912f01c3 Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Wed, 29 Oct 2025 17:03:08 -0400 Subject: [PATCH 02/11] refactor(two-factor-service) [PM-21204]: Build out stubs and add documentation. --- .../auth/abstractions/two-factor.service.ts | 127 ++++++++++++------ .../src/auth/services/two-factor.service.ts | 127 ++++++++++++------ 2 files changed, 170 insertions(+), 84 deletions(-) diff --git a/libs/common/src/auth/abstractions/two-factor.service.ts b/libs/common/src/auth/abstractions/two-factor.service.ts index b56c63479e53..4ed9f7be6bde 100644 --- a/libs/common/src/auth/abstractions/two-factor.service.ts +++ b/libs/common/src/auth/abstractions/two-factor.service.ts @@ -1,5 +1,15 @@ import { ListResponse } from "../../models/response/list.response"; import { TwoFactorProviderType } from "../enums/two-factor-provider-type"; +import { DisableTwoFactorAuthenticatorRequest } from "../models/request/disable-two-factor-authenticator.request"; +import { SecretVerificationRequest } from "../models/request/secret-verification.request"; +import { TwoFactorEmailRequest } from "../models/request/two-factor-email.request"; +import { TwoFactorProviderRequest } from "../models/request/two-factor-provider.request"; +import { UpdateTwoFactorAuthenticatorRequest } from "../models/request/update-two-factor-authenticator.request"; +import { UpdateTwoFactorDuoRequest } from "../models/request/update-two-factor-duo.request"; +import { UpdateTwoFactorEmailRequest } from "../models/request/update-two-factor-email.request"; +import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/update-two-factor-web-authn-delete.request"; +import { UpdateTwoFactorWebAuthnRequest } from "../models/request/update-two-factor-web-authn.request"; +import { UpdateTwoFactorYubikeyOtpRequest } from "../models/request/update-two-factor-yubikey-otp.request"; import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response"; import { TwoFactorAuthenticatorResponse } from "../models/response/two-factor-authenticator.response"; import { TwoFactorDuoResponse } from "../models/response/two-factor-duo.response"; @@ -11,7 +21,6 @@ import { TwoFactorWebAuthnResponse, } from "../models/response/two-factor-web-authn.response"; import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key.response"; -import { Verification } from "../types/verification"; export interface TwoFactorProviderDetails { type: TwoFactorProviderType; @@ -94,11 +103,12 @@ export abstract class TwoFactorService { * Requires user verification via master password or OTP. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link SecretVerificationRequest} to prove authentication. * @returns A promise that resolves to the authenticator configuration including the secret key. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ abstract getTwoFactorAuthenticator( - verification: Verification, + request: SecretVerificationRequest, ): Promise; /** @@ -106,20 +116,22 @@ export abstract class TwoFactorService { * Requires user verification via master password or OTP. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link SecretVerificationRequest} to prove authentication. * @returns A promise that resolves to the email two-factor configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract getTwoFactorEmail(verification: Verification): Promise; + abstract getTwoFactorEmail(request: SecretVerificationRequest): Promise; /** * Gets the Duo two-factor configuration for the current user from the API. * Requires user verification and an active premium subscription. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link SecretVerificationRequest} to prove authentication. * @returns A promise that resolves to the Duo configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract getTwoFactorDuo(verification: Verification): Promise; + abstract getTwoFactorDuo(request: SecretVerificationRequest): Promise; /** * Gets the Duo two-factor configuration for an organization from the API. @@ -127,12 +139,13 @@ export abstract class TwoFactorService { * Used for settings management. * * @param organizationId The ID of the organization. - * @param verification The verification information to authenticate the user. + * @param request The {@link SecretVerificationRequest} to prove authentication. * @returns A promise that resolves to the organization Duo configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ abstract getTwoFactorOrganizationDuo( organizationId: string, - verification: Verification, + request: SecretVerificationRequest, ): Promise; /** @@ -140,20 +153,26 @@ export abstract class TwoFactorService { * Requires user verification and an active premium subscription. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link SecretVerificationRequest} to prove authentication. * @returns A promise that resolves to the YubiKey configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract getTwoFactorYubiKey(verification: Verification): Promise; + abstract getTwoFactorYubiKey( + request: SecretVerificationRequest, + ): Promise; /** * Gets the WebAuthn (FIDO2) two-factor configuration for the current user from the API. * Requires user verification via master password or OTP. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link SecretVerificationRequest} to authentication. * @returns A promise that resolves to the WebAuthn configuration including registered credentials. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract getTwoFactorWebAuthn(verification: Verification): Promise; + abstract getTwoFactorWebAuthn( + request: SecretVerificationRequest, + ): Promise; /** * Gets a WebAuthn challenge for registering a new WebAuthn credential from the API. @@ -162,10 +181,13 @@ export abstract class TwoFactorService { * Requires user verification via master password or OTP. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link SecretVerificationRequest} to prove authentication. * @returns A promise that resolves to the credential creation options containing the challenge. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract getTwoFactorWebAuthnChallenge(verification: Verification): Promise; + abstract getTwoFactorWebAuthnChallenge( + request: SecretVerificationRequest, + ): Promise; /** * Gets the recovery code configuration for the current user from the API. @@ -173,10 +195,13 @@ export abstract class TwoFactorService { * Requires user verification via master password or OTP. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param verification The verification information to prove authentication. * @returns A promise that resolves to the recovery code configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract getTwoFactorRecover(verification: Verification): Promise; + abstract getTwoFactorRecover( + request: SecretVerificationRequest, + ): Promise; /** * Enables or updates the authenticator (TOTP) two-factor provider. @@ -184,11 +209,12 @@ export abstract class TwoFactorService { * The token must be generated by an authenticator app using the secret key. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link UpdateTwoFactorAuthenticatorRequest} to prove authentication. * @returns A promise that resolves to the updated authenticator configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ abstract putTwoFactorAuthenticator( - verification: Verification, + request: UpdateTwoFactorAuthenticatorRequest, ): Promise; /** @@ -196,11 +222,12 @@ export abstract class TwoFactorService { * Requires user verification token to confirm the operation. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link DisableTwoFactorAuthenticatorRequest} to prove authentication. * @returns A promise that resolves to the updated provider status. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ abstract deleteTwoFactorAuthenticator( - verification: Verification, + request: DisableTwoFactorAuthenticatorRequest, ): Promise; /** @@ -209,10 +236,11 @@ export abstract class TwoFactorService { * The token must match the code sent to the specified email address. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link UpdateTwoFactorEmailRequest} to prove authentication. * @returns A promise that resolves to the updated email two-factor configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract putTwoFactorEmail(verification: Verification): Promise; + abstract putTwoFactorEmail(request: UpdateTwoFactorEmailRequest): Promise; /** * Enables or updates the Duo two-factor provider for the current user. @@ -220,10 +248,11 @@ export abstract class TwoFactorService { * Requires user verification and an active premium subscription. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link UpdateTwoFactorDuoRequest} to prove authentication. * @returns A promise that resolves to the updated Duo configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract putTwoFactorDuo(verification: Verification): Promise; + abstract putTwoFactorDuo(request: UpdateTwoFactorDuoRequest): Promise; /** * Enables or updates the Duo two-factor provider for an organization. @@ -232,12 +261,13 @@ export abstract class TwoFactorService { * Used for settings management. * * @param organizationId The ID of the organization. - * @param verification The verification information to authenticate the user. + * @param request The {@link UpdateTwoFactorDuoRequest} to prove authentication. * @returns A promise that resolves to the updated organization Duo configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ abstract putTwoFactorOrganizationDuo( organizationId: string, - verification: Verification, + request: UpdateTwoFactorDuoRequest, ): Promise; /** @@ -247,10 +277,13 @@ export abstract class TwoFactorService { * Requires user verification and an active premium subscription. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link UpdateTwoFactorYubikeyOtpRequest} to prove authentication. * @returns A promise that resolves to the updated YubiKey configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract putTwoFactorYubiKey(verification: Verification): Promise; + abstract putTwoFactorYubiKey( + request: UpdateTwoFactorYubikeyOtpRequest, + ): Promise; /** * Registers a new WebAuthn (FIDO2) credential for two-factor authentication for the current user. @@ -259,10 +292,13 @@ export abstract class TwoFactorService { * Requires user verification via master password or OTP. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link UpdateTwoFactorWebAuthnRequest} to prove authentication. * @returns A promise that resolves to the updated WebAuthn configuration with the new credential. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract putTwoFactorWebAuthn(verification: Verification): Promise; + abstract putTwoFactorWebAuthn( + request: UpdateTwoFactorWebAuthnRequest, + ): Promise; /** * Removes a specific WebAuthn (FIDO2) credential from the user's account. @@ -271,10 +307,13 @@ export abstract class TwoFactorService { * Requires user verification via master password or OTP. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link UpdateTwoFactorWebAuthnDeleteRequest} to prove authentication. * @returns A promise that resolves to the updated WebAuthn configuration. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract deleteTwoFactorWebAuthn(verification: Verification): Promise; + abstract deleteTwoFactorWebAuthn( + request: UpdateTwoFactorWebAuthnDeleteRequest, + ): Promise; /** * Disables a specific two-factor provider for the current user. @@ -282,10 +321,13 @@ export abstract class TwoFactorService { * Requires user verification via master password or OTP. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link TwoFactorProviderRequest} to prove authentication. * @returns A promise that resolves to the updated provider status. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract putTwoFactorDisable(verification: Verification): Promise; + abstract putTwoFactorDisable( + request: TwoFactorProviderRequest, + ): Promise; /** * Disables a specific two-factor provider for an organization. @@ -294,12 +336,13 @@ export abstract class TwoFactorService { * Used for settings management. * * @param organizationId The ID of the organization. - * @param verification The verification information to authenticate the user. + * @param request The {@link TwoFactorProviderRequest} to prove authentication. * @returns A promise that resolves to the updated provider status. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ abstract putTwoFactorOrganizationDisable( organizationId: string, - verification: Verification, + request: TwoFactorProviderRequest, ): Promise; /** @@ -310,10 +353,11 @@ export abstract class TwoFactorService { * Requires user verification via master password or OTP. * Used for settings management. * - * @param verification The verification information to authenticate the user. + * @param request The {@link TwoFactorEmailRequest} to prove authentication. * @returns A promise that resolves when the verification email has been sent. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract postTwoFactorEmailSetup(verification: Verification): Promise; + abstract postTwoFactorEmailSetup(request: TwoFactorEmailRequest): Promise; /** * Sends a two-factor authentication code via email during the login flow. @@ -322,8 +366,9 @@ export abstract class TwoFactorService { * May be called without authentication for login scenarios. * Used during authentication flows. * - * @param verification The verification information to authenticate the user. + * @param request The {@link TwoFactorEmailRequest} to prove authentication. * @returns A promise that resolves when the authentication email has been sent. + * @remarks Use {@link UserVerificationService.buildRequest} to create the request object. */ - abstract postTwoFactorEmail(verification: Verification): Promise; + abstract postTwoFactorEmail(request: TwoFactorEmailRequest): Promise; } diff --git a/libs/common/src/auth/services/two-factor.service.ts b/libs/common/src/auth/services/two-factor.service.ts index a45f840f7f4b..5b04297d5258 100644 --- a/libs/common/src/auth/services/two-factor.service.ts +++ b/libs/common/src/auth/services/two-factor.service.ts @@ -12,6 +12,16 @@ import { TwoFactorService as TwoFactorServiceAbstraction, } from "../abstractions/two-factor.service"; import { TwoFactorProviderType } from "../enums/two-factor-provider-type"; +import { DisableTwoFactorAuthenticatorRequest } from "../models/request/disable-two-factor-authenticator.request"; +import { SecretVerificationRequest } from "../models/request/secret-verification.request"; +import { TwoFactorEmailRequest } from "../models/request/two-factor-email.request"; +import { TwoFactorProviderRequest } from "../models/request/two-factor-provider.request"; +import { UpdateTwoFactorAuthenticatorRequest } from "../models/request/update-two-factor-authenticator.request"; +import { UpdateTwoFactorDuoRequest } from "../models/request/update-two-factor-duo.request"; +import { UpdateTwoFactorEmailRequest } from "../models/request/update-two-factor-email.request"; +import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/update-two-factor-web-authn-delete.request"; +import { UpdateTwoFactorWebAuthnRequest } from "../models/request/update-two-factor-web-authn.request"; +import { UpdateTwoFactorYubikeyOtpRequest } from "../models/request/update-two-factor-yubikey-otp.request"; import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response"; import { TwoFactorAuthenticatorResponse } from "../models/response/two-factor-authenticator.response"; import { TwoFactorDuoResponse } from "../models/response/two-factor-duo.response"; @@ -24,7 +34,6 @@ import { } from "../models/response/two-factor-web-authn.response"; import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key.response"; import { TwoFactorApiService } from "../two-factor"; -import { Verification } from "../types/verification"; export const TwoFactorProviders: Partial> = { @@ -224,82 +233,114 @@ export class TwoFactorService implements TwoFactorServiceAbstraction { return firstValueFrom(this.providers$); } - async getEnabledTwoFactorProviders(): Promise> { + getEnabledTwoFactorProviders(): Promise> { return this.twoFactorApiService.getTwoFactorProviders(); } getTwoFactorOrganizationProviders( organizationId: string, ): Promise> { - throw new Error("Method not implemented."); + return this.twoFactorApiService.getTwoFactorOrganizationProviders(organizationId); } - getTwoFactorAuthenticator(verification: Verification): Promise { - throw new Error("Method not implemented."); + + getTwoFactorAuthenticator( + request: SecretVerificationRequest, + ): Promise { + return this.twoFactorApiService.getTwoFactorAuthenticator(request); } - getTwoFactorEmail(verification: Verification): Promise { - throw new Error("Method not implemented."); + + getTwoFactorEmail(request: SecretVerificationRequest): Promise { + return this.twoFactorApiService.getTwoFactorEmail(request); } - getTwoFactorDuo(verification: Verification): Promise { - throw new Error("Method not implemented."); + + getTwoFactorDuo(request: SecretVerificationRequest): Promise { + return this.twoFactorApiService.getTwoFactorDuo(request); } + getTwoFactorOrganizationDuo( organizationId: string, - verification: Verification, + request: SecretVerificationRequest, ): Promise { - throw new Error("Method not implemented."); + return this.twoFactorApiService.getTwoFactorOrganizationDuo(organizationId, request); } - getTwoFactorYubiKey(verification: Verification): Promise { - throw new Error("Method not implemented."); + + getTwoFactorYubiKey(request: SecretVerificationRequest): Promise { + return this.twoFactorApiService.getTwoFactorYubiKey(request); } - getTwoFactorWebAuthn(verification: Verification): Promise { - throw new Error("Method not implemented."); + + getTwoFactorWebAuthn(request: SecretVerificationRequest): Promise { + return this.twoFactorApiService.getTwoFactorWebAuthn(request); } - getTwoFactorWebAuthnChallenge(verification: Verification): Promise { - throw new Error("Method not implemented."); + + getTwoFactorWebAuthnChallenge(request: SecretVerificationRequest): Promise { + return this.twoFactorApiService.getTwoFactorWebAuthnChallenge(request); } - getTwoFactorRecover(verification: Verification): Promise { - throw new Error("Method not implemented."); + + getTwoFactorRecover(request: SecretVerificationRequest): Promise { + return this.twoFactorApiService.getTwoFactorRecover(request); } - putTwoFactorAuthenticator(verification: Verification): Promise { - throw new Error("Method not implemented."); + + putTwoFactorAuthenticator( + request: UpdateTwoFactorAuthenticatorRequest, + ): Promise { + return this.twoFactorApiService.putTwoFactorAuthenticator(request); } - deleteTwoFactorAuthenticator(verification: Verification): Promise { - throw new Error("Method not implemented."); + + deleteTwoFactorAuthenticator( + request: DisableTwoFactorAuthenticatorRequest, + ): Promise { + return this.twoFactorApiService.deleteTwoFactorAuthenticator(request); } - putTwoFactorEmail(verification: Verification): Promise { - throw new Error("Method not implemented."); + + putTwoFactorEmail(request: UpdateTwoFactorEmailRequest): Promise { + return this.twoFactorApiService.putTwoFactorEmail(request); } - putTwoFactorDuo(verification: Verification): Promise { - throw new Error("Method not implemented."); + + putTwoFactorDuo(request: UpdateTwoFactorDuoRequest): Promise { + return this.twoFactorApiService.putTwoFactorDuo(request); } + putTwoFactorOrganizationDuo( organizationId: string, - verification: Verification, + request: UpdateTwoFactorDuoRequest, ): Promise { - throw new Error("Method not implemented."); + return this.twoFactorApiService.putTwoFactorOrganizationDuo(organizationId, request); } - putTwoFactorYubiKey(verification: Verification): Promise { - throw new Error("Method not implemented."); + + putTwoFactorYubiKey( + request: UpdateTwoFactorYubikeyOtpRequest, + ): Promise { + return this.twoFactorApiService.putTwoFactorYubiKey(request); } - putTwoFactorWebAuthn(verification: Verification): Promise { - throw new Error("Method not implemented."); + + putTwoFactorWebAuthn( + request: UpdateTwoFactorWebAuthnRequest, + ): Promise { + return this.twoFactorApiService.putTwoFactorWebAuthn(request); } - deleteTwoFactorWebAuthn(verification: Verification): Promise { - throw new Error("Method not implemented."); + + deleteTwoFactorWebAuthn( + request: UpdateTwoFactorWebAuthnDeleteRequest, + ): Promise { + return this.twoFactorApiService.deleteTwoFactorWebAuthn(request); } - putTwoFactorDisable(verification: Verification): Promise { - throw new Error("Method not implemented."); + + putTwoFactorDisable(request: TwoFactorProviderRequest): Promise { + return this.twoFactorApiService.putTwoFactorDisable(request); } + putTwoFactorOrganizationDisable( organizationId: string, - verification: Verification, + request: TwoFactorProviderRequest, ): Promise { - throw new Error("Method not implemented."); + return this.twoFactorApiService.putTwoFactorOrganizationDisable(organizationId, request); } - postTwoFactorEmailSetup(verification: Verification): Promise { - throw new Error("Method not implemented."); + + postTwoFactorEmailSetup(request: TwoFactorEmailRequest): Promise { + return this.twoFactorApiService.postTwoFactorEmailSetup(request); } - postTwoFactorEmail(verification: Verification): Promise { - throw new Error("Method not implemented."); + + postTwoFactorEmail(request: TwoFactorEmailRequest): Promise { + return this.twoFactorApiService.postTwoFactorEmail(request); } } From 73437428da830f7bbfa0c81eca9bfd889c5e53cd Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Thu, 30 Oct 2025 08:55:51 -0400 Subject: [PATCH 03/11] refactor(two-factor-service) [PM-21204]: Update TwoFactorApiService call sites to use TwoFactorService. --- .../settings/two-factor-setup.component.ts | 8 ++++---- .../account/change-email.component.spec.ts | 10 +++++----- .../settings/account/change-email.component.ts | 6 +++--- ...-account-verify-devices-dialog.component.ts | 6 +++--- ...two-factor-setup-authenticator.component.ts | 10 +++++----- .../two-factor-setup-duo.component.ts | 10 +++++----- .../two-factor-setup-email.component.ts | 10 +++++----- .../two-factor-setup-method-base.component.ts | 12 ++++++------ .../two-factor-setup-webauthn.component.ts | 12 ++++++------ .../two-factor-setup-yubikey.component.ts | 8 ++++---- .../two-factor/two-factor-setup.component.ts | 6 +++--- .../two-factor/two-factor-verify.component.ts | 18 +++++++++--------- .../two-factor-auth-email.component.ts | 4 +--- 13 files changed, 59 insertions(+), 61 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts index 46e39a112bfc..6bcf6f7a7fd5 100644 --- a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts @@ -11,10 +11,10 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -37,7 +37,7 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent impleme tabbedHeader = false; constructor( dialogService: DialogService, - twoFactorApiService: TwoFactorApiService, + twoFactorService: TwoFactorService, messagingService: MessagingService, policyService: PolicyService, private route: ActivatedRoute, @@ -49,7 +49,7 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent impleme ) { super( dialogService, - twoFactorApiService, + twoFactorService, messagingService, policyService, billingAccountProfileStateService, @@ -118,7 +118,7 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent impleme } protected getTwoFactorProviders() { - return this.twoFactorApiService.getTwoFactorOrganizationProviders(this.organizationId); + return this.twoFactorService.getTwoFactorOrganizationProviders(this.organizationId); } protected filterProvider(type: TwoFactorProviderType): boolean { diff --git a/apps/web/src/app/auth/settings/account/change-email.component.spec.ts b/apps/web/src/app/auth/settings/account/change-email.component.spec.ts index 934de0f6453a..a3f905c2afe8 100644 --- a/apps/web/src/app/auth/settings/account/change-email.component.spec.ts +++ b/apps/web/src/app/auth/settings/account/change-email.component.spec.ts @@ -5,9 +5,9 @@ import { firstValueFrom, of } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorProviderResponse } from "@bitwarden/common/auth/models/response/two-factor-provider.response"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -23,14 +23,14 @@ describe("ChangeEmailComponent", () => { let fixture: ComponentFixture; let apiService: MockProxy; - let twoFactorApiService: MockProxy; + let twoFactorService: MockProxy; let accountService: FakeAccountService; let keyService: MockProxy; let kdfConfigService: MockProxy; beforeEach(async () => { apiService = mock(); - twoFactorApiService = mock(); + twoFactorService = mock(); keyService = mock(); kdfConfigService = mock(); accountService = mockAccountServiceWith("UserId" as UserId); @@ -40,7 +40,7 @@ describe("ChangeEmailComponent", () => { providers: [ { provide: AccountService, useValue: accountService }, { provide: ApiService, useValue: apiService }, - { provide: TwoFactorApiService, useValue: twoFactorApiService }, + { provide: TwoFactorService, useValue: twoFactorService }, { provide: I18nService, useValue: { t: (key: string) => key } }, { provide: KeyService, useValue: keyService }, { provide: MessagingService, useValue: mock() }, @@ -61,7 +61,7 @@ describe("ChangeEmailComponent", () => { describe("ngOnInit", () => { beforeEach(() => { - twoFactorApiService.getTwoFactorProviders.mockResolvedValue({ + twoFactorService.getEnabledTwoFactorProviders.mockResolvedValue({ data: [{ type: TwoFactorProviderType.Email, enabled: true } as TwoFactorProviderResponse], } as ListResponse); }); diff --git a/apps/web/src/app/auth/settings/account/change-email.component.ts b/apps/web/src/app/auth/settings/account/change-email.component.ts index ee29e0c8a9c0..045da7bb7ddf 100644 --- a/apps/web/src/app/auth/settings/account/change-email.component.ts +++ b/apps/web/src/app/auth/settings/account/change-email.component.ts @@ -4,11 +4,11 @@ import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { EmailTokenRequest } from "@bitwarden/common/auth/models/request/email-token.request"; import { EmailRequest } from "@bitwarden/common/auth/models/request/email.request"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { UserId } from "@bitwarden/common/types/guid"; @@ -40,7 +40,7 @@ export class ChangeEmailComponent implements OnInit { constructor( private accountService: AccountService, private apiService: ApiService, - private twoFactorApiService: TwoFactorApiService, + private twoFactorService: TwoFactorService, private i18nService: I18nService, private keyService: KeyService, private messagingService: MessagingService, @@ -52,7 +52,7 @@ export class ChangeEmailComponent implements OnInit { async ngOnInit() { this.userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); - const twoFactorProviders = await this.twoFactorApiService.getTwoFactorProviders(); + const twoFactorProviders = await this.twoFactorService.getEnabledTwoFactorProviders(); this.showTwoFactorEmailWarning = twoFactorProviders.data.some( (p) => p.type === TwoFactorProviderType.Email && p.enabled, ); diff --git a/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts b/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts index 01be46c45b3b..b04cdaf4c269 100644 --- a/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts +++ b/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts @@ -7,9 +7,9 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { UserVerificationFormInputComponent } from "@bitwarden/auth/angular"; import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { SetVerifyDevicesRequest } from "@bitwarden/common/auth/models/request/set-verify-devices.request"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { Verification } from "@bitwarden/common/auth/types/verification"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -66,7 +66,7 @@ export class SetAccountVerifyDevicesDialogComponent implements OnInit, OnDestroy private userVerificationService: UserVerificationService, private dialogRef: DialogRef, private toastService: ToastService, - private twoFactorApiService: TwoFactorApiService, + private twoFactorService: TwoFactorService, ) { this.accountService.accountVerifyNewDeviceLogin$ .pipe(takeUntil(this.destroy$)) @@ -76,7 +76,7 @@ export class SetAccountVerifyDevicesDialogComponent implements OnInit, OnDestroy } async ngOnInit() { - const twoFactorProviders = await this.twoFactorApiService.getTwoFactorProviders(); + const twoFactorProviders = await this.twoFactorService.getEnabledTwoFactorProviders(); this.has2faConfigured = twoFactorProviders.data.length > 0; } diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts index 20c3c742db63..0d566121aaac 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts @@ -7,12 +7,12 @@ import { firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { DisableTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/models/request/disable-two-factor-authenticator.request"; import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/models/request/update-two-factor-authenticator.request"; import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/auth/models/response/two-factor-authenticator.response"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -96,7 +96,7 @@ export class TwoFactorSetupAuthenticatorComponent constructor( @Inject(DIALOG_DATA) protected data: AuthResponse, private dialogRef: DialogRef, - twoFactorApiService: TwoFactorApiService, + twoFactorService: TwoFactorService, i18nService: I18nService, userVerificationService: UserVerificationService, private formBuilder: FormBuilder, @@ -108,7 +108,7 @@ export class TwoFactorSetupAuthenticatorComponent protected toastService: ToastService, ) { super( - twoFactorApiService, + twoFactorService, i18nService, platformUtilsService, logService, @@ -158,7 +158,7 @@ export class TwoFactorSetupAuthenticatorComponent request.key = this.key; request.userVerificationToken = this.userVerificationToken; - const response = await this.twoFactorApiService.putTwoFactorAuthenticator(request); + const response = await this.twoFactorService.putTwoFactorAuthenticator(request); await this.processResponse(response); this.onUpdated.emit(true); } @@ -178,7 +178,7 @@ export class TwoFactorSetupAuthenticatorComponent request.type = this.type; request.key = this.key; request.userVerificationToken = this.userVerificationToken; - await this.twoFactorApiService.deleteTwoFactorAuthenticator(request); + await this.twoFactorService.deleteTwoFactorAuthenticator(request); this.enabled = false; this.toastService.showToast({ variant: "success", diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts index 1a476f2206d1..7bf122c83927 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts @@ -2,11 +2,11 @@ import { CommonModule } from "@angular/common"; import { Component, EventEmitter, Inject, OnInit, Output } from "@angular/core"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { UpdateTwoFactorDuoRequest } from "@bitwarden/common/auth/models/request/update-two-factor-duo.request"; import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -67,7 +67,7 @@ export class TwoFactorSetupDuoComponent constructor( @Inject(DIALOG_DATA) protected data: TwoFactorDuoComponentConfig, - twoFactorApiService: TwoFactorApiService, + twoFactorService: TwoFactorService, i18nService: I18nService, platformUtilsService: PlatformUtilsService, logService: LogService, @@ -78,7 +78,7 @@ export class TwoFactorSetupDuoComponent protected toastService: ToastService, ) { super( - twoFactorApiService, + twoFactorService, i18nService, platformUtilsService, logService, @@ -143,12 +143,12 @@ export class TwoFactorSetupDuoComponent let response: TwoFactorDuoResponse; if (this.organizationId != null) { - response = await this.twoFactorApiService.putTwoFactorOrganizationDuo( + response = await this.twoFactorService.putTwoFactorOrganizationDuo( this.organizationId, request, ); } else { - response = await this.twoFactorApiService.putTwoFactorDuo(request); + response = await this.twoFactorService.putTwoFactorDuo(request); } this.processResponse(response); diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts index 4219fb0b6873..571725b89032 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts @@ -4,12 +4,12 @@ import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request"; import { UpdateTwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/update-two-factor-email.request"; import { TwoFactorEmailResponse } from "@bitwarden/common/auth/models/response/two-factor-email.response"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -70,7 +70,7 @@ export class TwoFactorSetupEmailComponent constructor( @Inject(DIALOG_DATA) protected data: AuthResponse, - twoFactorApiService: TwoFactorApiService, + twoFactorService: TwoFactorService, i18nService: I18nService, platformUtilsService: PlatformUtilsService, logService: LogService, @@ -82,7 +82,7 @@ export class TwoFactorSetupEmailComponent protected toastService: ToastService, ) { super( - twoFactorApiService, + twoFactorService, i18nService, platformUtilsService, logService, @@ -135,7 +135,7 @@ export class TwoFactorSetupEmailComponent sendEmail = async () => { const request = await this.buildRequestModel(TwoFactorEmailRequest); request.email = this.email; - this.emailPromise = this.twoFactorApiService.postTwoFactorEmailSetup(request); + this.emailPromise = this.twoFactorService.postTwoFactorEmailSetup(request); await this.emailPromise; this.sentEmail = this.email; }; @@ -145,7 +145,7 @@ export class TwoFactorSetupEmailComponent request.email = this.email; request.token = this.token; - const response = await this.twoFactorApiService.putTwoFactorEmail(request); + const response = await this.twoFactorService.putTwoFactorEmail(request); await this.processResponse(response); this.onUpdated.emit(true); } diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts index c614e45e5773..401dd3f3f7f9 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts @@ -1,11 +1,11 @@ import { Directive, EventEmitter, Output } from "@angular/core"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request"; import { TwoFactorProviderRequest } from "@bitwarden/common/auth/models/request/two-factor-provider.request"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AuthResponseBase } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -32,7 +32,7 @@ export abstract class TwoFactorSetupMethodBaseComponent { protected componentName = ""; constructor( - protected twoFactorApiService: TwoFactorApiService, + protected twoFactorService: TwoFactorService, protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService, protected logService: LogService, @@ -79,12 +79,12 @@ export abstract class TwoFactorSetupMethodBaseComponent { } request.type = this.type; if (this.organizationId != null) { - promise = this.twoFactorApiService.putTwoFactorOrganizationDisable( + promise = this.twoFactorService.putTwoFactorOrganizationDisable( this.organizationId, request, ); } else { - promise = this.twoFactorApiService.putTwoFactorDisable(request); + promise = this.twoFactorService.putTwoFactorDisable(request); } await promise; this.enabled = false; @@ -116,9 +116,9 @@ export abstract class TwoFactorSetupMethodBaseComponent { } request.type = this.type; if (this.organizationId != null) { - await this.twoFactorApiService.putTwoFactorOrganizationDisable(this.organizationId, request); + await this.twoFactorService.putTwoFactorOrganizationDisable(this.organizationId, request); } else { - await this.twoFactorApiService.putTwoFactorDisable(request); + await this.twoFactorService.putTwoFactorDisable(request); } this.enabled = false; this.toastService.showToast({ diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts index acf83ab278ed..3b5e55929d70 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts @@ -3,6 +3,7 @@ import { Component, Inject, NgZone } from "@angular/core"; import { FormControl, FormGroup, ReactiveFormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request"; @@ -12,7 +13,6 @@ import { ChallengeResponse, TwoFactorWebAuthnResponse, } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -81,7 +81,7 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom constructor( @Inject(DIALOG_DATA) protected data: AuthResponse, private dialogRef: DialogRef, - twoFactorApiService: TwoFactorApiService, + twoFactorService: TwoFactorService, i18nService: I18nService, platformUtilsService: PlatformUtilsService, private ngZone: NgZone, @@ -91,7 +91,7 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom toastService: ToastService, ) { super( - twoFactorApiService, + twoFactorService, i18nService, platformUtilsService, logService, @@ -129,7 +129,7 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom request.id = this.keyIdAvailable; request.name = this.formGroup.value.name || ""; - const response = await this.twoFactorApiService.putTwoFactorWebAuthn(request); + const response = await this.twoFactorService.putTwoFactorWebAuthn(request); this.processResponse(response); this.toastService.showToast({ title: this.i18nService.t("success"), @@ -165,7 +165,7 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom const request = await this.buildRequestModel(UpdateTwoFactorWebAuthnDeleteRequest); request.id = key.id; try { - key.removePromise = this.twoFactorApiService.deleteTwoFactorWebAuthn(request); + key.removePromise = this.twoFactorService.deleteTwoFactorWebAuthn(request); const response = await key.removePromise; key.removePromise = null; await this.processResponse(response); @@ -179,7 +179,7 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom return; } const request = await this.buildRequestModel(SecretVerificationRequest); - this.challengePromise = this.twoFactorApiService.getTwoFactorWebAuthnChallenge(request); + this.challengePromise = this.twoFactorService.getTwoFactorWebAuthnChallenge(request); const challenge = await this.challengePromise; this.readDevice(challenge); }; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts index 09fb1ad308fe..7ff5bfbe0f08 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts @@ -9,11 +9,11 @@ import { } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { UpdateTwoFactorYubikeyOtpRequest } from "@bitwarden/common/auth/models/request/update-two-factor-yubikey-otp.request"; import { TwoFactorYubiKeyResponse } from "@bitwarden/common/auth/models/response/two-factor-yubi-key.response"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -95,7 +95,7 @@ export class TwoFactorSetupYubiKeyComponent constructor( @Inject(DIALOG_DATA) protected data: AuthResponse, - twoFactorApiService: TwoFactorApiService, + twoFactorService: TwoFactorService, i18nService: I18nService, platformUtilsService: PlatformUtilsService, logService: LogService, @@ -105,7 +105,7 @@ export class TwoFactorSetupYubiKeyComponent protected toastService: ToastService, ) { super( - twoFactorApiService, + twoFactorService, i18nService, platformUtilsService, logService, @@ -178,7 +178,7 @@ export class TwoFactorSetupYubiKeyComponent request.key5 = keys != null && keys.length > 4 ? (keys[4]?.key ?? "") : ""; request.nfc = this.formGroup.value.anyKeyHasNfc ?? false; - this.processResponse(await this.twoFactorApiService.putTwoFactorYubiKey(request)); + this.processResponse(await this.twoFactorService.putTwoFactorYubiKey(request)); this.refreshFormArrayData(); this.toastService.showToast({ title: this.i18nService.t("success"), 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 ef4d647a7d0c..a34849f94d16 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 @@ -17,6 +17,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/auth/models/response/two-factor-authenticator.response"; import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response"; @@ -25,7 +26,6 @@ import { TwoFactorWebAuthnResponse } from "@bitwarden/common/auth/models/respons import { TwoFactorYubiKeyResponse } from "@bitwarden/common/auth/models/response/two-factor-yubi-key.response"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { TwoFactorProviders } from "@bitwarden/common/auth/services/two-factor.service"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; 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"; @@ -70,7 +70,7 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { constructor( protected dialogService: DialogService, - protected twoFactorApiService: TwoFactorApiService, + protected twoFactorService: TwoFactorService, protected messagingService: MessagingService, protected policyService: PolicyService, billingAccountProfileStateService: BillingAccountProfileStateService, @@ -272,7 +272,7 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { } protected getTwoFactorProviders() { - return this.twoFactorApiService.getTwoFactorProviders(); + return this.twoFactorService.getEnabledTwoFactorProviders(); } protected filterProvider(type: TwoFactorProviderType): boolean { diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts index 9baa93d38c06..843472877082 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts @@ -2,11 +2,11 @@ import { Component, EventEmitter, Inject, Output } from "@angular/core"; import { FormControl, FormGroup, ReactiveFormsModule } from "@angular/forms"; import { UserVerificationFormInputComponent } from "@bitwarden/auth/angular"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { TwoFactorResponse } from "@bitwarden/common/auth/types/two-factor-response"; import { Verification } from "@bitwarden/common/auth/types/verification"; @@ -59,7 +59,7 @@ export class TwoFactorVerifyComponent { constructor( @Inject(DIALOG_DATA) protected data: TwoFactorVerifyDialogData, private dialogRef: DialogRef, - private twoFactorApiService: TwoFactorApiService, + private twoFactorService: TwoFactorService, private i18nService: I18nService, private userVerificationService: UserVerificationService, ) { @@ -120,22 +120,22 @@ export class TwoFactorVerifyComponent { private apiCall(request: SecretVerificationRequest): Promise { switch (this.type) { case -1 as TwoFactorProviderType: - return this.twoFactorApiService.getTwoFactorRecover(request); + return this.twoFactorService.getTwoFactorRecover(request); case TwoFactorProviderType.Duo: case TwoFactorProviderType.OrganizationDuo: if (this.organizationId != null) { - return this.twoFactorApiService.getTwoFactorOrganizationDuo(this.organizationId, request); + return this.twoFactorService.getTwoFactorOrganizationDuo(this.organizationId, request); } else { - return this.twoFactorApiService.getTwoFactorDuo(request); + return this.twoFactorService.getTwoFactorDuo(request); } case TwoFactorProviderType.Email: - return this.twoFactorApiService.getTwoFactorEmail(request); + return this.twoFactorService.getTwoFactorEmail(request); case TwoFactorProviderType.WebAuthn: - return this.twoFactorApiService.getTwoFactorWebAuthn(request); + return this.twoFactorService.getTwoFactorWebAuthn(request); case TwoFactorProviderType.Authenticator: - return this.twoFactorApiService.getTwoFactorAuthenticator(request); + return this.twoFactorService.getTwoFactorAuthenticator(request); case TwoFactorProviderType.Yubikey: - return this.twoFactorApiService.getTwoFactorYubiKey(request); + return this.twoFactorService.getTwoFactorYubiKey(request); default: throw new Error(`Unknown two-factor type: ${this.type}`); } 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 000d391b62cc..89673a90dc0f 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 @@ -7,7 +7,6 @@ import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -68,7 +67,6 @@ export class TwoFactorAuthEmailComponent implements OnInit { protected loginStrategyService: LoginStrategyServiceAbstraction, protected platformUtilsService: PlatformUtilsService, protected logService: LogService, - protected twoFactorApiService: TwoFactorApiService, protected appIdService: AppIdService, private toastService: ToastService, private cacheService: TwoFactorAuthEmailComponentCacheService, @@ -137,7 +135,7 @@ export class TwoFactorAuthEmailComponent implements OnInit { request.deviceIdentifier = await this.appIdService.getAppId(); request.authRequestAccessCode = (await this.loginStrategyService.getAccessCode()) ?? ""; request.authRequestId = (await this.loginStrategyService.getAuthRequestId()) ?? ""; - this.emailPromise = this.twoFactorApiService.postTwoFactorEmail(request); + this.emailPromise = this.twoFactorService.postTwoFactorEmail(request); await this.emailPromise; this.emailSent = true; From f586f1b60277e4da711ec4b28a339218ca35753d Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Thu, 30 Oct 2025 11:02:33 -0400 Subject: [PATCH 04/11] refactor(two-fatcor) [PM-21204]: Remove deprecated and unused formPromise methods. --- .../two-factor-setup-method-base.component.ts | 52 ------------------- .../two-factor-setup-webauthn.component.ts | 1 - .../two-factor-setup-yubikey.component.ts | 3 -- .../two-factor/two-factor-setup.component.ts | 1 - .../two-factor/two-factor-verify.component.ts | 14 ++--- 5 files changed, 4 insertions(+), 67 deletions(-) diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts index 401dd3f3f7f9..f6b8bab0c9b0 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts @@ -47,58 +47,6 @@ export abstract class TwoFactorSetupMethodBaseComponent { this.authed = true; } - /** @deprecated used for formPromise flows.*/ - protected async enable(enableFunction: () => Promise) { - try { - await enableFunction(); - this.onUpdated.emit(true); - } catch (e) { - this.logService.error(e); - } - } - - /** - * @deprecated used for formPromise flows. - * TODO: Remove this method when formPromises are removed from all flows. - * */ - protected async disable(promise: Promise) { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "disable" }, - content: { key: "twoStepDisableDesc" }, - type: "warning", - }); - - if (!confirmed) { - return; - } - - try { - const request = await this.buildRequestModel(TwoFactorProviderRequest); - if (this.type === undefined) { - throw new Error("Two-factor provider type is required"); - } - request.type = this.type; - if (this.organizationId != null) { - promise = this.twoFactorService.putTwoFactorOrganizationDisable( - this.organizationId, - request, - ); - } else { - promise = this.twoFactorService.putTwoFactorDisable(request); - } - await promise; - this.enabled = false; - this.toastService.showToast({ - variant: "success", - title: "", - message: this.i18nService.t("twoStepDisabled"), - }); - this.onUpdated.emit(false); - } catch (e) { - this.logService.error(e); - } - } - protected async disableMethod() { const confirmed = await this.dialogService.openSimpleDialog({ title: { key: "disable" }, diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts index 3b5e55929d70..d432690a8bad 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts @@ -72,7 +72,6 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom webAuthnListening: boolean = false; webAuthnResponse: PublicKeyCredential | null = null; challengePromise: Promise | undefined; - formPromise: Promise | undefined; override componentName = "app-two-factor-webauthn"; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts index 7ff5bfbe0f08..31fb6362859e 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts @@ -74,9 +74,6 @@ export class TwoFactorSetupYubiKeyComponent keys: Key[] = []; anyKeyHasNfc = false; - formPromise: Promise | undefined; - disablePromise: Promise | undefined; - override componentName = "app-two-factor-yubikey"; formGroup: | FormGroup<{ 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 a34849f94d16..9316a9038319 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 @@ -60,7 +60,6 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { recoveryCodeWarningMessage: string; showPolicyWarning = false; loading = true; - formPromise: Promise; tabbedHeader = true; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts index 843472877082..5794f912fca7 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts @@ -49,8 +49,6 @@ export class TwoFactorVerifyComponent { // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onAuthed = new EventEmitter>(); - formPromise: Promise | undefined; - protected formGroup = new FormGroup({ secret: new FormControl(null), }); @@ -75,15 +73,11 @@ export class TwoFactorVerifyComponent { } const secret = this.formGroup.value.secret!; - this.formPromise = this.userVerificationService.buildRequest(secret).then((request) => { - hashedSecret = - secret.type === VerificationType.MasterPassword - ? request.masterPasswordHash - : request.otp; - return this.apiCall(request); - }); + const request = await this.userVerificationService.buildRequest(secret); + hashedSecret = + secret.type === VerificationType.MasterPassword ? request.masterPasswordHash : request.otp; - const response = await this.formPromise; + const response = await this.apiCall(request); this.dialogRef.close({ response: response, secret: hashedSecret, From 6bbe36e22d542d1ad5d2d79fa404d6c18e82fb0f Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:46:29 -0400 Subject: [PATCH 05/11] refactor(two-factor) [PM-21204]: Move 2FA-supporting services into common/auth/two-factor feature namespace. --- .../service-container/service-container.ts | 6 +- .../settings/two-factor-setup.component.ts | 2 +- .../account/change-email.component.spec.ts | 2 +- .../account/change-email.component.ts | 2 +- ...account-verify-devices-dialog.component.ts | 2 +- ...wo-factor-setup-authenticator.component.ts | 2 +- .../two-factor-setup-duo.component.ts | 2 +- .../two-factor-setup-email.component.ts | 2 +- .../two-factor-setup-method-base.component.ts | 2 +- .../two-factor-setup-webauthn.component.ts | 2 +- .../two-factor-setup-yubikey.component.ts | 2 +- .../two-factor/two-factor-setup.component.ts | 3 +- .../two-factor/two-factor-verify.component.ts | 2 +- apps/web/src/app/core/init.service.ts | 4 +- .../src/services/jslib-services.module.ts | 15 +- .../two-factor-auth-email.component.ts | 2 +- .../two-factor-auth-webauthn.component.ts | 2 +- .../two-factor-auth.component.spec.ts | 2 +- .../two-factor-auth.component.ts | 2 +- .../two-factor-auth.guard.spec.ts | 2 +- .../two-factor-auth/two-factor-auth.guard.ts | 2 +- .../two-factor-options.component.ts | 5 +- .../auth-request-login.strategy.spec.ts | 2 +- .../login-strategies/login.strategy.spec.ts | 2 +- .../common/login-strategies/login.strategy.ts | 2 +- .../password-login.strategy.spec.ts | 2 +- .../sso-login.strategy.spec.ts | 2 +- .../user-api-login.strategy.spec.ts | 2 +- .../webauthn-login.strategy.spec.ts | 2 +- .../login-strategy.service.spec.ts | 2 +- .../login-strategy.service.ts | 2 +- .../src/auth/two-factor/abstractions/index.ts | 2 + .../two-factor-api.service.ts | 0 .../abstractions/two-factor.service.ts | 163 +++++++++++++++--- libs/common/src/auth/two-factor/index.ts | 4 +- .../default-two-factor-api.service.spec.ts} | 0 .../default-two-factor-api.service.ts | 2 +- .../services/default-two-factor.service.ts} | 133 ++++---------- .../src/auth/two-factor/services/index.ts | 2 + 39 files changed, 225 insertions(+), 166 deletions(-) create mode 100644 libs/common/src/auth/two-factor/abstractions/index.ts rename libs/common/src/auth/two-factor/{ => abstractions}/two-factor-api.service.ts (100%) rename libs/common/src/auth/{ => two-factor}/abstractions/two-factor.service.ts (74%) rename libs/common/src/auth/two-factor/{two-factor-api.service.spec.ts => services/default-two-factor-api.service.spec.ts} (100%) rename libs/common/src/auth/two-factor/{ => services}/default-two-factor-api.service.ts (99%) rename libs/common/src/auth/{services/two-factor.service.ts => two-factor/services/default-two-factor.service.ts} (67%) create mode 100644 libs/common/src/auth/two-factor/services/index.ts diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 3c4ee55361fc..68229a5ed211 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -46,10 +46,9 @@ import { DefaultActiveUserAccessor } from "@bitwarden/common/auth/services/defau import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; import { MasterPasswordApiService } from "@bitwarden/common/auth/services/master-password/master-password-api.service.implementation"; import { TokenService } from "@bitwarden/common/auth/services/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service"; import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service"; import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service"; -import { TwoFactorApiService, DefaultTwoFactorApiService } from "@bitwarden/common/auth/two-factor"; +import { DefaultTwoFactorService, TwoFactorService , TwoFactorApiService, DefaultTwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { AutofillSettingsService, AutofillSettingsServiceAbstraction, @@ -620,10 +619,11 @@ export class ServiceContainer { this.stateProvider, ); - this.twoFactorService = new TwoFactorService( + this.twoFactorService = new DefaultTwoFactorService( this.i18nService, this.platformUtilsService, this.globalStateProvider, + this.twoFactorApiService, ); const sdkClientFactory = flagEnabled("sdk") diff --git a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts index 6bcf6f7a7fd5..5e31be784a3d 100644 --- a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts @@ -11,10 +11,10 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; diff --git a/apps/web/src/app/auth/settings/account/change-email.component.spec.ts b/apps/web/src/app/auth/settings/account/change-email.component.spec.ts index a3f905c2afe8..56f2bfe21127 100644 --- a/apps/web/src/app/auth/settings/account/change-email.component.spec.ts +++ b/apps/web/src/app/auth/settings/account/change-email.component.spec.ts @@ -5,9 +5,9 @@ import { firstValueFrom, of } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorProviderResponse } from "@bitwarden/common/auth/models/response/two-factor-provider.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; diff --git a/apps/web/src/app/auth/settings/account/change-email.component.ts b/apps/web/src/app/auth/settings/account/change-email.component.ts index 045da7bb7ddf..3daf2240fb2a 100644 --- a/apps/web/src/app/auth/settings/account/change-email.component.ts +++ b/apps/web/src/app/auth/settings/account/change-email.component.ts @@ -4,11 +4,11 @@ import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { EmailTokenRequest } from "@bitwarden/common/auth/models/request/email-token.request"; import { EmailRequest } from "@bitwarden/common/auth/models/request/email.request"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { UserId } from "@bitwarden/common/types/guid"; diff --git a/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts b/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts index b04cdaf4c269..97f50df24c8e 100644 --- a/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts +++ b/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts @@ -7,9 +7,9 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { UserVerificationFormInputComponent } from "@bitwarden/auth/angular"; import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { SetVerifyDevicesRequest } from "@bitwarden/common/auth/models/request/set-verify-devices.request"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { Verification } from "@bitwarden/common/auth/types/verification"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts index 0d566121aaac..d93a59474450 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts @@ -7,12 +7,12 @@ import { firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { DisableTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/models/request/disable-two-factor-authenticator.request"; import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/models/request/update-two-factor-authenticator.request"; import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/auth/models/response/two-factor-authenticator.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts index 7bf122c83927..b4c8ece92a74 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts @@ -2,11 +2,11 @@ import { CommonModule } from "@angular/common"; import { Component, EventEmitter, Inject, OnInit, Output } from "@angular/core"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { UpdateTwoFactorDuoRequest } from "@bitwarden/common/auth/models/request/update-two-factor-duo.request"; import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts index 571725b89032..1402d6b89694 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts @@ -4,12 +4,12 @@ import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request"; import { UpdateTwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/update-two-factor-email.request"; import { TwoFactorEmailResponse } from "@bitwarden/common/auth/models/response/two-factor-email.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts index f6b8bab0c9b0..17dd4f7e4cb0 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts @@ -1,11 +1,11 @@ import { Directive, EventEmitter, Output } from "@angular/core"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request"; import { TwoFactorProviderRequest } from "@bitwarden/common/auth/models/request/two-factor-provider.request"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { AuthResponseBase } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts index d432690a8bad..11ba59559021 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts @@ -3,7 +3,6 @@ import { Component, Inject, NgZone } from "@angular/core"; import { FormControl, FormGroup, ReactiveFormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request"; @@ -13,6 +12,7 @@ import { ChallengeResponse, TwoFactorWebAuthnResponse, } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts index 31fb6362859e..a58c659796dc 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts @@ -9,11 +9,11 @@ import { } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { UpdateTwoFactorYubikeyOtpRequest } from "@bitwarden/common/auth/models/request/update-two-factor-yubikey-otp.request"; import { TwoFactorYubiKeyResponse } from "@bitwarden/common/auth/models/response/two-factor-yubi-key.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; 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 9316a9038319..9f7588634dd5 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 @@ -17,7 +17,6 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/auth/models/response/two-factor-authenticator.response"; import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response"; @@ -25,7 +24,7 @@ import { TwoFactorEmailResponse } from "@bitwarden/common/auth/models/response/t import { TwoFactorWebAuthnResponse } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response"; import { TwoFactorYubiKeyResponse } from "@bitwarden/common/auth/models/response/two-factor-yubi-key.response"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { TwoFactorProviders } from "@bitwarden/common/auth/services/two-factor.service"; +import { TwoFactorService, TwoFactorProviders } from "@bitwarden/common/auth/two-factor"; 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"; diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts index 5794f912fca7..3ad84e8f5b40 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts @@ -2,11 +2,11 @@ import { Component, EventEmitter, Inject, Output } from "@angular/core"; import { FormControl, FormGroup, ReactiveFormsModule } from "@angular/forms"; import { UserVerificationFormInputComponent } from "@bitwarden/auth/angular"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { TwoFactorResponse } from "@bitwarden/common/auth/types/two-factor-response"; import { Verification } from "@bitwarden/common/auth/types/verification"; diff --git a/apps/web/src/app/core/init.service.ts b/apps/web/src/app/core/init.service.ts index 6b03913ef7af..71e3578a7c6b 100644 --- a/apps/web/src/app/core/init.service.ts +++ b/apps/web/src/app/core/init.service.ts @@ -6,7 +6,7 @@ import { AbstractThemingService } from "@bitwarden/angular/platform/services/the import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DefaultVaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -31,7 +31,7 @@ export class InitService { private vaultTimeoutService: DefaultVaultTimeoutService, private i18nService: I18nServiceAbstraction, private eventUploadService: EventUploadServiceAbstraction, - private twoFactorService: TwoFactorServiceAbstraction, + private twoFactorService: TwoFactorService, private keyService: KeyServiceAbstraction, private themingService: AbstractThemingService, private encryptService: EncryptService, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 291db0886fdc..ad329baa61f2 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -100,7 +100,6 @@ import { MasterPasswordApiService as MasterPasswordApiServiceAbstraction } from import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction"; import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { WebAuthnLoginApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login-api.service.abstraction"; @@ -123,13 +122,17 @@ import { OrganizationInviteService } from "@bitwarden/common/auth/services/organ import { PasswordResetEnrollmentServiceImplementation } from "@bitwarden/common/auth/services/password-reset-enrollment.service.implementation"; import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service"; import { TokenService } from "@bitwarden/common/auth/services/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service"; import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service"; import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service"; import { WebAuthnLoginApiService } from "@bitwarden/common/auth/services/webauthn-login/webauthn-login-api.service"; import { WebAuthnLoginPrfKeyService } from "@bitwarden/common/auth/services/webauthn-login/webauthn-login-prf-key.service"; import { WebAuthnLoginService } from "@bitwarden/common/auth/services/webauthn-login/webauthn-login.service"; -import { TwoFactorApiService, DefaultTwoFactorApiService } from "@bitwarden/common/auth/two-factor"; +import { + TwoFactorApiService, + DefaultTwoFactorApiService, + TwoFactorService, + DefaultTwoFactorService, +} from "@bitwarden/common/auth/two-factor"; import { AutofillSettingsService, AutofillSettingsServiceAbstraction, @@ -519,7 +522,7 @@ const safeProviders: SafeProvider[] = [ KeyConnectorServiceAbstraction, EnvironmentService, StateServiceAbstraction, - TwoFactorServiceAbstraction, + TwoFactorService, I18nServiceAbstraction, EncryptService, PasswordStrengthServiceAbstraction, @@ -1162,8 +1165,8 @@ const safeProviders: SafeProvider[] = [ deps: [StateProvider], }), safeProvider({ - provide: TwoFactorServiceAbstraction, - useClass: TwoFactorService, + provide: TwoFactorService, + useClass: DefaultTwoFactorService, deps: [ I18nServiceAbstraction, PlatformUtilsServiceAbstraction, 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 89673a90dc0f..8976236f48c4 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 @@ -4,9 +4,9 @@ import { ReactiveFormsModule, FormsModule, FormControl } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts index 71a91ec20e72..54d6f7c5f775 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts @@ -6,8 +6,8 @@ import { firstValueFrom } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { WebAuthnIFrame } from "@bitwarden/common/auth/webauthn-iframe"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; 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 5d36fd384cac..af9fb03e01e2 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 @@ -18,12 +18,12 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type"; 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 { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction, 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 52f20b046012..8e10539823d8 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,12 +32,12 @@ import { import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; 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 { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.guard.spec.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.guard.spec.ts index 116da73173f8..37fbf6bf7942 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.guard.spec.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.guard.spec.ts @@ -4,8 +4,8 @@ import { provideRouter, Router } from "@angular/router"; import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { LoginStrategyServiceAbstraction } from "../../common"; diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.guard.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.guard.ts index 2aec0bae4416..a7ed4010ef5b 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.guard.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.guard.ts @@ -8,7 +8,7 @@ import { } from "@angular/router"; import { firstValueFrom } from "rxjs"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { LoginStrategyServiceAbstraction } from "../../common"; diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-options.component.ts b/libs/auth/src/angular/two-factor-auth/two-factor-options.component.ts index d0ad9be61031..d8b2ab2508b1 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-options.component.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-options.component.ts @@ -9,11 +9,8 @@ import { TwoFactorAuthWebAuthnIcon, TwoFactorAuthYubicoIcon, } from "@bitwarden/assets/svg"; -import { - TwoFactorProviderDetails, - TwoFactorService, -} from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; +import { TwoFactorProviderDetails, TwoFactorService } from "@bitwarden/common/auth/two-factor"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports import { diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts index 0b19fecdc4e0..b536ae0dc4bd 100644 --- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts @@ -3,8 +3,8 @@ import { BehaviorSubject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; diff --git a/libs/auth/src/common/login-strategies/login.strategy.spec.ts b/libs/auth/src/common/login-strategies/login.strategy.spec.ts index a23f8034238a..e9eed27d5a1f 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.spec.ts @@ -5,7 +5,6 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; @@ -16,6 +15,7 @@ import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/id import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response"; import { MasterPasswordPolicyResponse } from "@bitwarden/common/auth/models/response/master-password-policy.response"; import { IUserDecryptionOptionsServerResponse } from "@bitwarden/common/auth/models/response/user-decryption-options/user-decryption-options.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; diff --git a/libs/auth/src/common/login-strategies/login.strategy.ts b/libs/auth/src/common/login-strategies/login.strategy.ts index 7ad5cd243537..35f13246593c 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.ts @@ -3,7 +3,6 @@ import { BehaviorSubject, filter, firstValueFrom, timeout, Observable } from "rx import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; @@ -16,6 +15,7 @@ import { WebAuthnLoginTokenRequest } from "@bitwarden/common/auth/models/request import { IdentityDeviceVerificationResponse } from "@bitwarden/common/auth/models/response/identity-device-verification.response"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; diff --git a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts index 8e3867d1b36c..51aa13aab5eb 100644 --- a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts @@ -5,12 +5,12 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response"; import { MasterPasswordPolicyResponse } from "@bitwarden/common/auth/models/response/master-password-policy.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service"; diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts index bce05b35e62c..03de5f36c2d3 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts @@ -3,12 +3,12 @@ import { BehaviorSubject, of } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; import { IUserDecryptionOptionsServerResponse } from "@bitwarden/common/auth/models/response/user-decryption-options/user-decryption-options.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncryptedString } from "@bitwarden/common/key-management/crypto/models/enc-string"; diff --git a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts index a6446401f70e..9bf282dee110 100644 --- a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts @@ -3,7 +3,7 @@ import { BehaviorSubject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts index 25ae8a31ef69..53bc1c57905a 100644 --- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts @@ -3,11 +3,11 @@ import { BehaviorSubject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; import { IUserDecryptionOptionsServerResponse } from "@bitwarden/common/auth/models/response/user-decryption-options/user-decryption-options.response"; import { WebAuthnLoginAssertionResponseRequest } from "@bitwarden/common/auth/services/webauthn-login/request/webauthn-login-assertion-response.request"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service"; diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts index 8ddee96dd574..34d6f73a59dc 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts @@ -4,13 +4,13 @@ import { BehaviorSubject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response"; import { PreloginResponse } from "@bitwarden/common/auth/models/response/prelogin.response"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts index 6900e5e58722..69297f535b86 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts @@ -13,10 +13,10 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; diff --git a/libs/common/src/auth/two-factor/abstractions/index.ts b/libs/common/src/auth/two-factor/abstractions/index.ts new file mode 100644 index 000000000000..00789048ebec --- /dev/null +++ b/libs/common/src/auth/two-factor/abstractions/index.ts @@ -0,0 +1,2 @@ +export * from "./two-factor-api.service"; +export * from "./two-factor.service"; diff --git a/libs/common/src/auth/two-factor/two-factor-api.service.ts b/libs/common/src/auth/two-factor/abstractions/two-factor-api.service.ts similarity index 100% rename from libs/common/src/auth/two-factor/two-factor-api.service.ts rename to libs/common/src/auth/two-factor/abstractions/two-factor-api.service.ts diff --git a/libs/common/src/auth/abstractions/two-factor.service.ts b/libs/common/src/auth/two-factor/abstractions/two-factor.service.ts similarity index 74% rename from libs/common/src/auth/abstractions/two-factor.service.ts rename to libs/common/src/auth/two-factor/abstractions/two-factor.service.ts index 4ed9f7be6bde..bc48a9821bb1 100644 --- a/libs/common/src/auth/abstractions/two-factor.service.ts +++ b/libs/common/src/auth/two-factor/abstractions/two-factor.service.ts @@ -1,35 +1,158 @@ -import { ListResponse } from "../../models/response/list.response"; -import { TwoFactorProviderType } from "../enums/two-factor-provider-type"; -import { DisableTwoFactorAuthenticatorRequest } from "../models/request/disable-two-factor-authenticator.request"; -import { SecretVerificationRequest } from "../models/request/secret-verification.request"; -import { TwoFactorEmailRequest } from "../models/request/two-factor-email.request"; -import { TwoFactorProviderRequest } from "../models/request/two-factor-provider.request"; -import { UpdateTwoFactorAuthenticatorRequest } from "../models/request/update-two-factor-authenticator.request"; -import { UpdateTwoFactorDuoRequest } from "../models/request/update-two-factor-duo.request"; -import { UpdateTwoFactorEmailRequest } from "../models/request/update-two-factor-email.request"; -import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/update-two-factor-web-authn-delete.request"; -import { UpdateTwoFactorWebAuthnRequest } from "../models/request/update-two-factor-web-authn.request"; -import { UpdateTwoFactorYubikeyOtpRequest } from "../models/request/update-two-factor-yubikey-otp.request"; -import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response"; -import { TwoFactorAuthenticatorResponse } from "../models/response/two-factor-authenticator.response"; -import { TwoFactorDuoResponse } from "../models/response/two-factor-duo.response"; -import { TwoFactorEmailResponse } from "../models/response/two-factor-email.response"; -import { TwoFactorProviderResponse } from "../models/response/two-factor-provider.response"; -import { TwoFactorRecoverResponse } from "../models/response/two-factor-recover.response"; +import { ListResponse } from "../../../models/response/list.response"; +import { KeyDefinition, TWO_FACTOR_MEMORY } from "../../../platform/state"; +import { TwoFactorProviderType } from "../../enums/two-factor-provider-type"; +import { DisableTwoFactorAuthenticatorRequest } from "../../models/request/disable-two-factor-authenticator.request"; +import { SecretVerificationRequest } from "../../models/request/secret-verification.request"; +import { TwoFactorEmailRequest } from "../../models/request/two-factor-email.request"; +import { TwoFactorProviderRequest } from "../../models/request/two-factor-provider.request"; +import { UpdateTwoFactorAuthenticatorRequest } from "../../models/request/update-two-factor-authenticator.request"; +import { UpdateTwoFactorDuoRequest } from "../../models/request/update-two-factor-duo.request"; +import { UpdateTwoFactorEmailRequest } from "../../models/request/update-two-factor-email.request"; +import { UpdateTwoFactorWebAuthnDeleteRequest } from "../../models/request/update-two-factor-web-authn-delete.request"; +import { UpdateTwoFactorWebAuthnRequest } from "../../models/request/update-two-factor-web-authn.request"; +import { UpdateTwoFactorYubikeyOtpRequest } from "../../models/request/update-two-factor-yubikey-otp.request"; +import { IdentityTwoFactorResponse } from "../../models/response/identity-two-factor.response"; +import { TwoFactorAuthenticatorResponse } from "../../models/response/two-factor-authenticator.response"; +import { TwoFactorDuoResponse } from "../../models/response/two-factor-duo.response"; +import { TwoFactorEmailResponse } from "../../models/response/two-factor-email.response"; +import { TwoFactorProviderResponse } from "../../models/response/two-factor-provider.response"; +import { TwoFactorRecoverResponse } from "../../models/response/two-factor-recover.response"; import { ChallengeResponse, TwoFactorWebAuthnResponse, -} from "../models/response/two-factor-web-authn.response"; -import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key.response"; +} from "../../models/response/two-factor-web-authn.response"; +import { TwoFactorYubiKeyResponse } from "../../models/response/two-factor-yubi-key.response"; +/** + * Metadata and display information for a two-factor authentication provider. + * Used by UI components to render provider selection and configuration screens. + */ export interface TwoFactorProviderDetails { + /** The unique identifier for this provider type. */ type: TwoFactorProviderType; + + /** + * Display name for the provider, localized via {@link TwoFactorService.init}. + * Examples: "Authenticator App", "Email", "YubiKey". + */ name: string; + + /** + * User-facing description explaining what this provider is and how it works. + * Localized via {@link TwoFactorService.init}. + */ description: string; + + /** + * Selection priority during login when multiple providers are available. + * Higher values are preferred. Used to determine the default provider. + * Range: 0 (lowest) to 10 (highest). + */ priority: number; + + /** + * Display order in provider lists within settings UI. + * Lower values appear first (1 = first position). + */ sort: number; + + /** + * Whether this provider requires an active premium subscription. + * Premium providers: Duo (personal), YubiKey. + * Organization providers (e.g., OrganizationDuo) do not require personal premium. + */ premium: boolean; } + +/** + * Registry of all supported two-factor authentication providers with their metadata. + * Strings (name, description) are initialized as null and populated with localized + * translations when {@link TwoFactorService.init} is called during application startup. + * + * @remarks + * This constant is mutated during initialization. Components should not access it before + * the service's init() method has been called. + * + * @example + * ```typescript + * // During app init + * twoFactorService.init(); + * + * // In components + * const authenticator = TwoFactorProviders[TwoFactorProviderType.Authenticator]; + * console.log(authenticator.name); // "Authenticator App" (localized) + * ``` + */ +export const TwoFactorProviders: Partial> = + { + [TwoFactorProviderType.Authenticator]: { + type: TwoFactorProviderType.Authenticator, + name: null as string, + description: null as string, + priority: 1, + sort: 2, + premium: false, + }, + [TwoFactorProviderType.Yubikey]: { + type: TwoFactorProviderType.Yubikey, + name: null as string, + description: null as string, + priority: 3, + sort: 4, + premium: true, + }, + [TwoFactorProviderType.Duo]: { + type: TwoFactorProviderType.Duo, + name: "Duo", + description: null as string, + priority: 2, + sort: 5, + premium: true, + }, + [TwoFactorProviderType.OrganizationDuo]: { + type: TwoFactorProviderType.OrganizationDuo, + name: "Duo (Organization)", + description: null as string, + priority: 10, + sort: 6, + premium: false, + }, + [TwoFactorProviderType.Email]: { + type: TwoFactorProviderType.Email, + name: null as string, + description: null as string, + priority: 0, + sort: 1, + premium: false, + }, + [TwoFactorProviderType.WebAuthn]: { + type: TwoFactorProviderType.WebAuthn, + name: null as string, + description: null as string, + priority: 4, + sort: 3, + premium: false, + }, + }; + +// Memory storage as only required during authentication process +export const PROVIDERS = KeyDefinition.record, TwoFactorProviderType>( + TWO_FACTOR_MEMORY, + "providers", + { + deserializer: (obj) => obj, + }, +); + +// Memory storage as only required during authentication process +export const SELECTED_PROVIDER = new KeyDefinition( + TWO_FACTOR_MEMORY, + "selected", + { + deserializer: (obj) => obj, + }, +); + export abstract class TwoFactorService { /** * Initializes the client-side's TwoFactorProviders const with translations. diff --git a/libs/common/src/auth/two-factor/index.ts b/libs/common/src/auth/two-factor/index.ts index 85e072403b76..fd6edf0ac3c3 100644 --- a/libs/common/src/auth/two-factor/index.ts +++ b/libs/common/src/auth/two-factor/index.ts @@ -1,2 +1,2 @@ -export { TwoFactorApiService } from "./two-factor-api.service"; -export { DefaultTwoFactorApiService } from "./default-two-factor-api.service"; +export * from "./abstractions"; +export * from "./services"; diff --git a/libs/common/src/auth/two-factor/two-factor-api.service.spec.ts b/libs/common/src/auth/two-factor/services/default-two-factor-api.service.spec.ts similarity index 100% rename from libs/common/src/auth/two-factor/two-factor-api.service.spec.ts rename to libs/common/src/auth/two-factor/services/default-two-factor-api.service.spec.ts diff --git a/libs/common/src/auth/two-factor/default-two-factor-api.service.ts b/libs/common/src/auth/two-factor/services/default-two-factor-api.service.ts similarity index 99% rename from libs/common/src/auth/two-factor/default-two-factor-api.service.ts rename to libs/common/src/auth/two-factor/services/default-two-factor-api.service.ts index 93f7b207922a..b9778e460ea6 100644 --- a/libs/common/src/auth/two-factor/default-two-factor-api.service.ts +++ b/libs/common/src/auth/two-factor/services/default-two-factor-api.service.ts @@ -22,7 +22,7 @@ import { TwoFactorYubiKeyResponse } from "@bitwarden/common/auth/models/response import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { TwoFactorApiService } from "./two-factor-api.service"; +import { TwoFactorApiService } from "../abstractions/two-factor-api.service"; export class DefaultTwoFactorApiService implements TwoFactorApiService { constructor(private apiService: ApiService) {} diff --git a/libs/common/src/auth/services/two-factor.service.ts b/libs/common/src/auth/two-factor/services/default-two-factor.service.ts similarity index 67% rename from libs/common/src/auth/services/two-factor.service.ts rename to libs/common/src/auth/two-factor/services/default-two-factor.service.ts index 5b04297d5258..2acc4a7e9399 100644 --- a/libs/common/src/auth/services/two-factor.service.ts +++ b/libs/common/src/auth/two-factor/services/default-two-factor.service.ts @@ -2,110 +2,43 @@ // @ts-strict-ignore import { firstValueFrom, map } from "rxjs"; -import { ListResponse } from "../../models/response/list.response"; -import { I18nService } from "../../platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service"; -import { Utils } from "../../platform/misc/utils"; -import { GlobalStateProvider, KeyDefinition, TWO_FACTOR_MEMORY } from "../../platform/state"; +import { TwoFactorApiService } from ".."; +import { ListResponse } from "../../../models/response/list.response"; +import { I18nService } from "../../../platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service"; +import { Utils } from "../../../platform/misc/utils"; +import { GlobalStateProvider } from "../../../platform/state"; +import { TwoFactorProviderType } from "../../enums/two-factor-provider-type"; +import { DisableTwoFactorAuthenticatorRequest } from "../../models/request/disable-two-factor-authenticator.request"; +import { SecretVerificationRequest } from "../../models/request/secret-verification.request"; +import { TwoFactorEmailRequest } from "../../models/request/two-factor-email.request"; +import { TwoFactorProviderRequest } from "../../models/request/two-factor-provider.request"; +import { UpdateTwoFactorAuthenticatorRequest } from "../../models/request/update-two-factor-authenticator.request"; +import { UpdateTwoFactorDuoRequest } from "../../models/request/update-two-factor-duo.request"; +import { UpdateTwoFactorEmailRequest } from "../../models/request/update-two-factor-email.request"; +import { UpdateTwoFactorWebAuthnDeleteRequest } from "../../models/request/update-two-factor-web-authn-delete.request"; +import { UpdateTwoFactorWebAuthnRequest } from "../../models/request/update-two-factor-web-authn.request"; +import { UpdateTwoFactorYubikeyOtpRequest } from "../../models/request/update-two-factor-yubikey-otp.request"; +import { IdentityTwoFactorResponse } from "../../models/response/identity-two-factor.response"; +import { TwoFactorAuthenticatorResponse } from "../../models/response/two-factor-authenticator.response"; +import { TwoFactorDuoResponse } from "../../models/response/two-factor-duo.response"; +import { TwoFactorEmailResponse } from "../../models/response/two-factor-email.response"; +import { TwoFactorProviderResponse } from "../../models/response/two-factor-provider.response"; +import { TwoFactorRecoverResponse } from "../../models/response/two-factor-recover.response"; import { + TwoFactorWebAuthnResponse, + ChallengeResponse, +} from "../../models/response/two-factor-web-authn.response"; +import { TwoFactorYubiKeyResponse } from "../../models/response/two-factor-yubi-key.response"; +import { + PROVIDERS, + SELECTED_PROVIDER, TwoFactorProviderDetails, + TwoFactorProviders, TwoFactorService as TwoFactorServiceAbstraction, } from "../abstractions/two-factor.service"; -import { TwoFactorProviderType } from "../enums/two-factor-provider-type"; -import { DisableTwoFactorAuthenticatorRequest } from "../models/request/disable-two-factor-authenticator.request"; -import { SecretVerificationRequest } from "../models/request/secret-verification.request"; -import { TwoFactorEmailRequest } from "../models/request/two-factor-email.request"; -import { TwoFactorProviderRequest } from "../models/request/two-factor-provider.request"; -import { UpdateTwoFactorAuthenticatorRequest } from "../models/request/update-two-factor-authenticator.request"; -import { UpdateTwoFactorDuoRequest } from "../models/request/update-two-factor-duo.request"; -import { UpdateTwoFactorEmailRequest } from "../models/request/update-two-factor-email.request"; -import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/update-two-factor-web-authn-delete.request"; -import { UpdateTwoFactorWebAuthnRequest } from "../models/request/update-two-factor-web-authn.request"; -import { UpdateTwoFactorYubikeyOtpRequest } from "../models/request/update-two-factor-yubikey-otp.request"; -import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response"; -import { TwoFactorAuthenticatorResponse } from "../models/response/two-factor-authenticator.response"; -import { TwoFactorDuoResponse } from "../models/response/two-factor-duo.response"; -import { TwoFactorEmailResponse } from "../models/response/two-factor-email.response"; -import { TwoFactorProviderResponse } from "../models/response/two-factor-provider.response"; -import { TwoFactorRecoverResponse } from "../models/response/two-factor-recover.response"; -import { - TwoFactorWebAuthnResponse, - ChallengeResponse, -} from "../models/response/two-factor-web-authn.response"; -import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key.response"; -import { TwoFactorApiService } from "../two-factor"; - -export const TwoFactorProviders: Partial> = - { - [TwoFactorProviderType.Authenticator]: { - type: TwoFactorProviderType.Authenticator, - name: null as string, - description: null as string, - priority: 1, - sort: 2, - premium: false, - }, - [TwoFactorProviderType.Yubikey]: { - type: TwoFactorProviderType.Yubikey, - name: null as string, - description: null as string, - priority: 3, - sort: 4, - premium: true, - }, - [TwoFactorProviderType.Duo]: { - type: TwoFactorProviderType.Duo, - name: "Duo", - description: null as string, - priority: 2, - sort: 5, - premium: true, - }, - [TwoFactorProviderType.OrganizationDuo]: { - type: TwoFactorProviderType.OrganizationDuo, - name: "Duo (Organization)", - description: null as string, - priority: 10, - sort: 6, - premium: false, - }, - [TwoFactorProviderType.Email]: { - type: TwoFactorProviderType.Email, - name: null as string, - description: null as string, - priority: 0, - sort: 1, - premium: false, - }, - [TwoFactorProviderType.WebAuthn]: { - type: TwoFactorProviderType.WebAuthn, - name: null as string, - description: null as string, - priority: 4, - sort: 3, - premium: false, - }, - }; - -// Memory storage as only required during authentication process -export const PROVIDERS = KeyDefinition.record, TwoFactorProviderType>( - TWO_FACTOR_MEMORY, - "providers", - { - deserializer: (obj) => obj, - }, -); - -// Memory storage as only required during authentication process -export const SELECTED_PROVIDER = new KeyDefinition( - TWO_FACTOR_MEMORY, - "selected", - { - deserializer: (obj) => obj, - }, -); - -export class TwoFactorService implements TwoFactorServiceAbstraction { + +export class DefaultTwoFactorService implements TwoFactorServiceAbstraction { private providersState = this.globalStateProvider.get(PROVIDERS); private selectedState = this.globalStateProvider.get(SELECTED_PROVIDER); readonly providers$ = this.providersState.state$.pipe( diff --git a/libs/common/src/auth/two-factor/services/index.ts b/libs/common/src/auth/two-factor/services/index.ts new file mode 100644 index 000000000000..283f7b34631d --- /dev/null +++ b/libs/common/src/auth/two-factor/services/index.ts @@ -0,0 +1,2 @@ +export * from "./default-two-factor-api.service"; +export * from "./default-two-factor.service"; From 97e2d691c7b8fb6c80916eb939f349a74db4f86d Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:00:20 -0400 Subject: [PATCH 06/11] refactor(two-factor) [PM-21204]: Update imports for service/init containers. --- apps/browser/src/popup/services/init.service.ts | 2 +- apps/cli/src/auth/commands/login.command.ts | 3 +-- apps/desktop/src/app/services/init.service.ts | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index 1de1731013de..91b6f0ff1050 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -2,7 +2,7 @@ import { DOCUMENT } from "@angular/common"; import { inject, Inject, Injectable } from "@angular/core"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; -import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/apps/cli/src/auth/commands/login.command.ts b/apps/cli/src/auth/commands/login.command.ts index aa43b353f9c8..f81d3e566c2f 100644 --- a/apps/cli/src/auth/commands/login.command.ts +++ b/apps/cli/src/auth/commands/login.command.ts @@ -20,7 +20,6 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { AccountService } 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 { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; @@ -28,7 +27,7 @@ import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/ide import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request"; import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request"; import { UpdateTempPasswordRequest } from "@bitwarden/common/auth/models/request/update-temp-password.request"; -import { TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; +import { TwoFactorService , TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { ClientType } from "@bitwarden/common/enums"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index ae633bd4a69f..73c4d38d3b2b 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -6,7 +6,7 @@ import { AbstractThemingService } from "@bitwarden/angular/platform/services/the import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service"; +import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DefaultVaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -39,7 +39,7 @@ export class InitService { private vaultTimeoutService: DefaultVaultTimeoutService, private i18nService: I18nServiceAbstraction, private eventUploadService: EventUploadServiceAbstraction, - private twoFactorService: TwoFactorServiceAbstraction, + private twoFactorService: TwoFactorService, private notificationsService: ServerNotificationsService, private platformUtilsService: PlatformUtilsServiceAbstraction, private stateService: StateServiceAbstraction, From d1b465b29bf0af11a1451875cf97c29eedbf5317 Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:16:58 -0500 Subject: [PATCH 07/11] feat(two-factor) [PM-21204]: Add a disabling flow for Premium 2FA when enabled on a non-Premium account. --- .../settings/two-factor-setup.component.ts | 7 ++- .../two-factor-setup.component.html | 29 +++++++---- .../two-factor/two-factor-setup.component.ts | 52 ++++++++++++++++++- 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts index 5e31be784a3d..95081af3a532 100644 --- a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts @@ -11,6 +11,7 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -20,7 +21,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs 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"; -import { DialogRef, DialogService } from "@bitwarden/components"; +import { DialogRef, DialogService, ToastService } from "@bitwarden/components"; import { TwoFactorSetupDuoComponent } from "../../../auth/settings/two-factor/two-factor-setup-duo.component"; import { TwoFactorSetupComponent as BaseTwoFactorSetupComponent } from "../../../auth/settings/two-factor/two-factor-setup.component"; @@ -46,6 +47,8 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent impleme protected accountService: AccountService, configService: ConfigService, i18nService: I18nService, + protected userVerificationService: UserVerificationService, + protected toastService: ToastService, ) { super( dialogService, @@ -56,6 +59,8 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent impleme accountService, configService, i18nService, + userVerificationService, + toastService, ); } diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.html b/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.html index 16c3dcb3cda8..c95008f03a72 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.html +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.html @@ -71,15 +71,26 @@

{{ p.description }}
- + @if (p.premium && p.enabled && !(canAccessPremium$ | async)) { + + } @else { + + } 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 9f7588634dd5..d6f70876229e 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 @@ -13,11 +13,14 @@ import { } from "rxjs"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; +import { UserVerificationDialogComponent } from "@bitwarden/auth/angular"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; +import { TwoFactorProviderRequest } from "@bitwarden/common/auth/models/request/two-factor-provider.request"; import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/auth/models/response/two-factor-authenticator.response"; import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response"; import { TwoFactorEmailResponse } from "@bitwarden/common/auth/models/response/two-factor-email.response"; @@ -31,7 +34,7 @@ import { ProductTierType } from "@bitwarden/common/billing/enums"; 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"; -import { DialogRef, DialogService, ItemModule } from "@bitwarden/components"; +import { DialogRef, DialogService, ItemModule, ToastService } from "@bitwarden/components"; import { HeaderModule } from "../../../layouts/header/header.module"; import { SharedModule } from "../../../shared/shared.module"; @@ -75,6 +78,8 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { protected accountService: AccountService, protected configService: ConfigService, protected i18nService: I18nService, + protected userVerificationService: UserVerificationService, + protected toastService: ToastService, ) { this.canAccessPremium$ = this.accountService.activeAccount$.pipe( switchMap((account) => @@ -149,6 +154,51 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { return await lastValueFrom(twoFactorVerifyDialogRef.closed); } + /** + * For users who enabled a premium-only 2fa provider, + * they should still be allowed to disable that provider + * (without otherwise modifying) if they no longer have + * premium access [PM-21204] + * @param type the 2FA Provider Type + */ + async disablePremium2faTypeForNonPremiumUser(type: TwoFactorProviderType) { + // Use UserVerificationDialogComponent instead of TwoFactorVerifyComponent + // because the latter makes GET API calls that require premium for YubiKey/Duo. + // The disable endpoint only requires user verification, not provider configuration. + const result = await UserVerificationDialogComponent.open(this.dialogService, { + title: "twoStepLogin", + bodyText: "disableXTwoStepLogin", + verificationType: { + type: "custom", + verificationFn: async (secret) => { + const request = await this.userVerificationService.buildRequest( + secret, + TwoFactorProviderRequest, + ); + request.type = type; + + await this.twoFactorService.putTwoFactorDisable(request); + return true; + }, + }, + }); + + if (result.userAction === "cancel") { + return; + } + + if (!result.verificationSuccess) { + return; + } + + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("twoStepDisabled"), + }); + this.updateStatus(false, type); + } + async manage(type: TwoFactorProviderType) { // clear any existing subscriptions before creating a new one this.twoFactorSetupSubscription?.unsubscribe(); From 4a6549db5938fd9d3a1543b507cde3647139a524 Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:15:25 -0500 Subject: [PATCH 08/11] fix(two-factor-service) [PM-21204]: Fix type-safety of module constants. --- .../abstractions/two-factor.service.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libs/common/src/auth/two-factor/abstractions/two-factor.service.ts b/libs/common/src/auth/two-factor/abstractions/two-factor.service.ts index bc48a9821bb1..4cba40716e10 100644 --- a/libs/common/src/auth/two-factor/abstractions/two-factor.service.ts +++ b/libs/common/src/auth/two-factor/abstractions/two-factor.service.ts @@ -35,13 +35,13 @@ export interface TwoFactorProviderDetails { * Display name for the provider, localized via {@link TwoFactorService.init}. * Examples: "Authenticator App", "Email", "YubiKey". */ - name: string; + name: string | null; /** * User-facing description explaining what this provider is and how it works. * Localized via {@link TwoFactorService.init}. */ - description: string; + description: string | null; /** * Selection priority during login when multiple providers are available. @@ -87,16 +87,16 @@ export const TwoFactorProviders: Partial Date: Wed, 5 Nov 2025 16:20:26 -0500 Subject: [PATCH 09/11] fix(multiple) [PM-21204]: Prettier. --- apps/cli/src/auth/commands/login.command.ts | 2 +- apps/cli/src/service-container/service-container.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/cli/src/auth/commands/login.command.ts b/apps/cli/src/auth/commands/login.command.ts index f81d3e566c2f..d0ab062d0b30 100644 --- a/apps/cli/src/auth/commands/login.command.ts +++ b/apps/cli/src/auth/commands/login.command.ts @@ -27,7 +27,7 @@ import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/ide import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request"; import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request"; import { UpdateTempPasswordRequest } from "@bitwarden/common/auth/models/request/update-temp-password.request"; -import { TwoFactorService , TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; +import { TwoFactorService, TwoFactorApiService } from "@bitwarden/common/auth/two-factor"; import { ClientType } from "@bitwarden/common/enums"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 68229a5ed211..077d610ba0e8 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -48,7 +48,12 @@ import { MasterPasswordApiService } from "@bitwarden/common/auth/services/master import { TokenService } from "@bitwarden/common/auth/services/token.service"; import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service"; import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service"; -import { DefaultTwoFactorService, TwoFactorService , TwoFactorApiService, DefaultTwoFactorApiService } from "@bitwarden/common/auth/two-factor"; +import { + DefaultTwoFactorService, + TwoFactorService, + TwoFactorApiService, + DefaultTwoFactorApiService, +} from "@bitwarden/common/auth/two-factor"; import { AutofillSettingsService, AutofillSettingsServiceAbstraction, From 801b186391d8bbd4241985f235851e9db3e751fe Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Thu, 20 Nov 2025 07:16:59 -0500 Subject: [PATCH 10/11] fix(user-verification-dialog) [PM-21204]: Remove bodyText configuration for this use. --- .../app/auth/settings/two-factor/two-factor-setup.component.ts | 1 - 1 file changed, 1 deletion(-) 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 61b61db9245b..a85bf65ab745 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 @@ -166,7 +166,6 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { // The disable endpoint only requires user verification, not provider configuration. const result = await UserVerificationDialogComponent.open(this.dialogService, { title: "twoStepLogin", - bodyText: "disableXTwoStepLogin", verificationType: { type: "custom", verificationFn: async (secret) => { From ca3bcfb3b46aebf1905d17a446243730ccaf2b9c Mon Sep 17 00:00:00 2001 From: enmande <3836813+enmande@users.noreply.github.com> Date: Thu, 20 Nov 2025 09:07:20 -0500 Subject: [PATCH 11/11] fix(user-verification-dialog) [PM-21204]: Improve the error message displayed to the user. --- apps/web/src/locales/en/messages.json | 3 +++ .../user-verification/user-verification-dialog.component.ts | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 1b0460e2aa68..17b887c46434 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -12161,5 +12161,8 @@ }, "confirmNoSelectedCriticalApplicationsDesc": { "message": "Are you sure you want to continue?" + }, + "userVerificationFailed": { + "message": "User verification failed." } } diff --git a/libs/auth/src/angular/user-verification/user-verification-dialog.component.ts b/libs/auth/src/angular/user-verification/user-verification-dialog.component.ts index 09d428d4ba7f..1304df2a7638 100644 --- a/libs/auth/src/angular/user-verification/user-verification-dialog.component.ts +++ b/libs/auth/src/angular/user-verification/user-verification-dialog.component.ts @@ -277,13 +277,13 @@ export class UserVerificationDialogComponent { }); } } - } catch (e) { + } catch { // Catch handles OTP and MP verification scenarios as those throw errors on verification failure instead of returning false like PIN and biometrics. this.invalidSecret = true; this.toastService.showToast({ variant: "error", title: this.i18nService.t("error"), - message: e.message, + message: this.i18nService.t("userVerificationFailed"), }); return; }