1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 13:23:34 +00:00

feat(new-device-verification-screen): (Auth) [PM-17489] Back Button on New Device Verification Screen (#16599)

On Web and Desktop, show back button on `NewDeviceVerificationComponent` (route `/device-verification`). Do not show it on Extension, because Extension already has a back button in the header.
This commit is contained in:
rr-bw
2025-10-01 12:57:41 -07:00
committed by GitHub
parent 420b26776b
commit cae58232e5
10 changed files with 109 additions and 4 deletions

View File

@@ -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);
});
});
});

View File

@@ -0,0 +1,13 @@
import {
DefaultNewDeviceVerificationComponentService,
NewDeviceVerificationComponentService,
} from "@bitwarden/auth/angular";
export class ExtensionNewDeviceVerificationComponentService
extends DefaultNewDeviceVerificationComponentService
implements NewDeviceVerificationComponentService
{
showBackButton() {
return false;
}
}

View File

@@ -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({

View File

@@ -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({

View File

@@ -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";

View File

@@ -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);
});
});
});

View File

@@ -0,0 +1,9 @@
import { NewDeviceVerificationComponentService } from "./new-device-verification-component.service";
export class DefaultNewDeviceVerificationComponentService
implements NewDeviceVerificationComponentService
{
showBackButton() {
return true;
}
}

View File

@@ -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;
}

View File

@@ -22,7 +22,7 @@
{{ "resendCode" | i18n }}
</button>
<div class="tw-flex tw-mt-4">
<div class="tw-grid tw-gap-3 tw-mt-4">
<button
bitButton
bitFormButton
@@ -33,5 +33,13 @@
>
{{ "continueLoggingIn" | i18n }}
</button>
@if (showBackButton) {
<div class="tw-text-center">{{ "or" | i18n }}</div>
<button type="button" bitButton block buttonType="secondary" (click)="goBack()">
{{ "back" | i18n }}
</button>
}
</div>
</form>

View File

@@ -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<void>();
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();
}
}