diff --git a/apps/browser/src/auth/services/new-device-verification/extension-new-device-verification-component.service.spec.ts b/apps/browser/src/auth/services/new-device-verification/extension-new-device-verification-component.service.spec.ts new file mode 100644 index 00000000000..7c91cae3fcb --- /dev/null +++ b/apps/browser/src/auth/services/new-device-verification/extension-new-device-verification-component.service.spec.ts @@ -0,0 +1,21 @@ +import { ExtensionNewDeviceVerificationComponentService } from "./extension-new-device-verification-component.service"; + +describe("ExtensionNewDeviceVerificationComponentService", () => { + let sut: ExtensionNewDeviceVerificationComponentService; + + beforeEach(() => { + sut = new ExtensionNewDeviceVerificationComponentService(); + }); + + it("should instantiate the service", () => { + expect(sut).not.toBeFalsy(); + }); + + describe("showBackButton()", () => { + it("should return false", () => { + const result = sut.showBackButton(); + + expect(result).toBe(false); + }); + }); +}); diff --git a/apps/browser/src/auth/services/new-device-verification/extension-new-device-verification-component.service.ts b/apps/browser/src/auth/services/new-device-verification/extension-new-device-verification-component.service.ts new file mode 100644 index 00000000000..05e60fc8dad --- /dev/null +++ b/apps/browser/src/auth/services/new-device-verification/extension-new-device-verification-component.service.ts @@ -0,0 +1,13 @@ +import { + DefaultNewDeviceVerificationComponentService, + NewDeviceVerificationComponentService, +} from "@bitwarden/auth/angular"; + +export class ExtensionNewDeviceVerificationComponentService + extends DefaultNewDeviceVerificationComponentService + implements NewDeviceVerificationComponentService +{ + showBackButton() { + return false; + } +} diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index ef4dd0be090..7a10dc2343f 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -29,6 +29,7 @@ import { TwoFactorAuthDuoComponentService, TwoFactorAuthWebAuthnComponentService, SsoComponentService, + NewDeviceVerificationComponentService, } from "@bitwarden/auth/angular"; import { LockService, @@ -36,6 +37,7 @@ import { SsoUrlService, LogoutService, } from "@bitwarden/auth/common"; +import { ExtensionNewDeviceVerificationComponentService } from "@bitwarden/browser/auth/services/new-device-verification/extension-new-device-verification-component.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -710,6 +712,11 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultCipherArchiveService, deps: [CipherService, ApiService, BillingAccountProfileStateService, ConfigService], }), + safeProvider({ + provide: NewDeviceVerificationComponentService, + useClass: ExtensionNewDeviceVerificationComponentService, + deps: [], + }), ]; @NgModule({ diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 8c727a98d11..3304c54a86f 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -20,11 +20,13 @@ import { import { DefaultLoginComponentService, DefaultLoginDecryptionOptionsService, + DefaultNewDeviceVerificationComponentService, DefaultRegistrationFinishService, DefaultTwoFactorAuthComponentService, DefaultTwoFactorAuthWebAuthnComponentService, LoginComponentService, LoginDecryptionOptionsService, + NewDeviceVerificationComponentService, RegistrationFinishService as RegistrationFinishServiceAbstraction, TwoFactorAuthComponentService, TwoFactorAuthWebAuthnComponentService, @@ -1646,6 +1648,11 @@ const safeProviders: SafeProvider[] = [ ConfigService, ], }), + safeProvider({ + provide: NewDeviceVerificationComponentService, + useClass: DefaultNewDeviceVerificationComponentService, + deps: [], + }), ]; @NgModule({ diff --git a/libs/auth/src/angular/index.ts b/libs/auth/src/angular/index.ts index fdd06509511..454a9091c25 100644 --- a/libs/auth/src/angular/index.ts +++ b/libs/auth/src/angular/index.ts @@ -59,6 +59,8 @@ export * from "./two-factor-auth"; // device verification export * from "./new-device-verification/new-device-verification.component"; +export * from "./new-device-verification/new-device-verification-component.service"; +export * from "./new-device-verification/default-new-device-verification-component.service"; // validators export * from "./validators/compare-inputs.validator"; diff --git a/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.spec.ts b/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.spec.ts new file mode 100644 index 00000000000..a2ea26268ea --- /dev/null +++ b/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.spec.ts @@ -0,0 +1,21 @@ +import { DefaultNewDeviceVerificationComponentService } from "./default-new-device-verification-component.service"; + +describe("DefaultNewDeviceVerificationComponentService", () => { + let sut: DefaultNewDeviceVerificationComponentService; + + beforeEach(() => { + sut = new DefaultNewDeviceVerificationComponentService(); + }); + + it("should instantiate the service", () => { + expect(sut).not.toBeFalsy(); + }); + + describe("showBackButton()", () => { + it("should return true", () => { + const result = sut.showBackButton(); + + expect(result).toBe(true); + }); + }); +}); diff --git a/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.ts b/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.ts new file mode 100644 index 00000000000..88ea652bc4b --- /dev/null +++ b/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.ts @@ -0,0 +1,9 @@ +import { NewDeviceVerificationComponentService } from "./new-device-verification-component.service"; + +export class DefaultNewDeviceVerificationComponentService + implements NewDeviceVerificationComponentService +{ + showBackButton() { + return true; + } +} diff --git a/libs/auth/src/angular/new-device-verification/new-device-verification-component.service.ts b/libs/auth/src/angular/new-device-verification/new-device-verification-component.service.ts new file mode 100644 index 00000000000..c34fc40ef00 --- /dev/null +++ b/libs/auth/src/angular/new-device-verification/new-device-verification-component.service.ts @@ -0,0 +1,8 @@ +export abstract class NewDeviceVerificationComponentService { + /** + * States whether component should show a back button. Can be overridden by client-specific component services. + * - Default = `true` + * - Extension = `false` (because Extension shows a back button in the header instead) + */ + abstract showBackButton: () => boolean; +} diff --git a/libs/auth/src/angular/new-device-verification/new-device-verification.component.html b/libs/auth/src/angular/new-device-verification/new-device-verification.component.html index e731f3afcb6..814c48db0fc 100644 --- a/libs/auth/src/angular/new-device-verification/new-device-verification.component.html +++ b/libs/auth/src/angular/new-device-verification/new-device-verification.component.html @@ -22,7 +22,7 @@ {{ "resendCode" | i18n }} -
+
+ + @if (showBackButton) { +
{{ "or" | i18n }}
+ + + }
diff --git a/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts b/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts index 6362b901fc8..2211b3390a7 100644 --- a/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts +++ b/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts @@ -1,4 +1,4 @@ -import { CommonModule } from "@angular/common"; +import { CommonModule, Location } from "@angular/common"; import { Component, OnDestroy, OnInit } from "@angular/core"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; import { Router } from "@angular/router"; @@ -11,7 +11,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. @@ -26,6 +25,8 @@ import { import { LoginStrategyServiceAbstraction } from "../../common/abstractions/login-strategy.service"; +import { NewDeviceVerificationComponentService } from "./new-device-verification-component.service"; + /** * Component for verifying a new device via a one-time password (OTP). */ @@ -57,6 +58,7 @@ export class NewDeviceVerificationComponent implements OnInit, OnDestroy { protected disableRequestOTP = false; private destroy$ = new Subject(); protected authenticationSessionTimeoutRoute = "/authentication-timeout"; + protected showBackButton = true; constructor( private router: Router, @@ -66,12 +68,15 @@ export class NewDeviceVerificationComponent implements OnInit, OnDestroy { private logService: LogService, private i18nService: I18nService, private loginSuccessHandlerService: LoginSuccessHandlerService, - private configService: ConfigService, private accountService: AccountService, private masterPasswordService: MasterPasswordServiceAbstraction, + private newDeviceVerificationComponentService: NewDeviceVerificationComponentService, + private location: Location, ) {} async ngOnInit() { + this.showBackButton = this.newDeviceVerificationComponentService.showBackButton(); + // Redirect to timeout route if session expires this.loginStrategyService.authenticationSessionTimeout$ .pipe(takeUntil(this.destroy$)) @@ -179,4 +184,8 @@ export class NewDeviceVerificationComponent implements OnInit, OnDestroy { codeControl.markAsTouched(); } }; + + protected goBack() { + this.location.back(); + } }