mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +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:
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import {
|
||||||
|
DefaultNewDeviceVerificationComponentService,
|
||||||
|
NewDeviceVerificationComponentService,
|
||||||
|
} from "@bitwarden/auth/angular";
|
||||||
|
|
||||||
|
export class ExtensionNewDeviceVerificationComponentService
|
||||||
|
extends DefaultNewDeviceVerificationComponentService
|
||||||
|
implements NewDeviceVerificationComponentService
|
||||||
|
{
|
||||||
|
showBackButton() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
TwoFactorAuthDuoComponentService,
|
TwoFactorAuthDuoComponentService,
|
||||||
TwoFactorAuthWebAuthnComponentService,
|
TwoFactorAuthWebAuthnComponentService,
|
||||||
SsoComponentService,
|
SsoComponentService,
|
||||||
|
NewDeviceVerificationComponentService,
|
||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import {
|
import {
|
||||||
LockService,
|
LockService,
|
||||||
@@ -36,6 +37,7 @@ import {
|
|||||||
SsoUrlService,
|
SsoUrlService,
|
||||||
LogoutService,
|
LogoutService,
|
||||||
} from "@bitwarden/auth/common";
|
} 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.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";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
@@ -710,6 +712,11 @@ const safeProviders: SafeProvider[] = [
|
|||||||
useClass: DefaultCipherArchiveService,
|
useClass: DefaultCipherArchiveService,
|
||||||
deps: [CipherService, ApiService, BillingAccountProfileStateService, ConfigService],
|
deps: [CipherService, ApiService, BillingAccountProfileStateService, ConfigService],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: NewDeviceVerificationComponentService,
|
||||||
|
useClass: ExtensionNewDeviceVerificationComponentService,
|
||||||
|
deps: [],
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ import {
|
|||||||
import {
|
import {
|
||||||
DefaultLoginComponentService,
|
DefaultLoginComponentService,
|
||||||
DefaultLoginDecryptionOptionsService,
|
DefaultLoginDecryptionOptionsService,
|
||||||
|
DefaultNewDeviceVerificationComponentService,
|
||||||
DefaultRegistrationFinishService,
|
DefaultRegistrationFinishService,
|
||||||
DefaultTwoFactorAuthComponentService,
|
DefaultTwoFactorAuthComponentService,
|
||||||
DefaultTwoFactorAuthWebAuthnComponentService,
|
DefaultTwoFactorAuthWebAuthnComponentService,
|
||||||
LoginComponentService,
|
LoginComponentService,
|
||||||
LoginDecryptionOptionsService,
|
LoginDecryptionOptionsService,
|
||||||
|
NewDeviceVerificationComponentService,
|
||||||
RegistrationFinishService as RegistrationFinishServiceAbstraction,
|
RegistrationFinishService as RegistrationFinishServiceAbstraction,
|
||||||
TwoFactorAuthComponentService,
|
TwoFactorAuthComponentService,
|
||||||
TwoFactorAuthWebAuthnComponentService,
|
TwoFactorAuthWebAuthnComponentService,
|
||||||
@@ -1646,6 +1648,11 @@ const safeProviders: SafeProvider[] = [
|
|||||||
ConfigService,
|
ConfigService,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: NewDeviceVerificationComponentService,
|
||||||
|
useClass: DefaultNewDeviceVerificationComponentService,
|
||||||
|
deps: [],
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ export * from "./two-factor-auth";
|
|||||||
|
|
||||||
// device verification
|
// device verification
|
||||||
export * from "./new-device-verification/new-device-verification.component";
|
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
|
// validators
|
||||||
export * from "./validators/compare-inputs.validator";
|
export * from "./validators/compare-inputs.validator";
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { NewDeviceVerificationComponentService } from "./new-device-verification-component.service";
|
||||||
|
|
||||||
|
export class DefaultNewDeviceVerificationComponentService
|
||||||
|
implements NewDeviceVerificationComponentService
|
||||||
|
{
|
||||||
|
showBackButton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
{{ "resendCode" | i18n }}
|
{{ "resendCode" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="tw-flex tw-mt-4">
|
<div class="tw-grid tw-gap-3 tw-mt-4">
|
||||||
<button
|
<button
|
||||||
bitButton
|
bitButton
|
||||||
bitFormButton
|
bitFormButton
|
||||||
@@ -33,5 +33,13 @@
|
|||||||
>
|
>
|
||||||
{{ "continueLoggingIn" | i18n }}
|
{{ "continueLoggingIn" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
@if (showBackButton) {
|
||||||
|
<div class="tw-text-center">{{ "or" | i18n }}</div>
|
||||||
|
|
||||||
|
<button type="button" bitButton block buttonType="secondary" (click)="goBack()">
|
||||||
|
{{ "back" | i18n }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule, Location } from "@angular/common";
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
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 { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.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.
|
// 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 { 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).
|
* 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;
|
protected disableRequestOTP = false;
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
protected authenticationSessionTimeoutRoute = "/authentication-timeout";
|
protected authenticationSessionTimeoutRoute = "/authentication-timeout";
|
||||||
|
protected showBackButton = true;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@@ -66,12 +68,15 @@ export class NewDeviceVerificationComponent implements OnInit, OnDestroy {
|
|||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private loginSuccessHandlerService: LoginSuccessHandlerService,
|
private loginSuccessHandlerService: LoginSuccessHandlerService,
|
||||||
private configService: ConfigService,
|
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
private masterPasswordService: MasterPasswordServiceAbstraction,
|
private masterPasswordService: MasterPasswordServiceAbstraction,
|
||||||
|
private newDeviceVerificationComponentService: NewDeviceVerificationComponentService,
|
||||||
|
private location: Location,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
this.showBackButton = this.newDeviceVerificationComponentService.showBackButton();
|
||||||
|
|
||||||
// Redirect to timeout route if session expires
|
// Redirect to timeout route if session expires
|
||||||
this.loginStrategyService.authenticationSessionTimeout$
|
this.loginStrategyService.authenticationSessionTimeout$
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
@@ -179,4 +184,8 @@ export class NewDeviceVerificationComponent implements OnInit, OnDestroy {
|
|||||||
codeControl.markAsTouched();
|
codeControl.markAsTouched();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected goBack() {
|
||||||
|
this.location.back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user