mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 10:13:31 +00:00
feat(auth): [PM-9693] Refresh LoginDecryptionOptionsComponent (#11782)
Creates a refreshed and consolidated `LoginDecryptionOptionsComponent` for use on all visual clients, which will be used when the `UnauthenticatedExtensionUIRefresh` feature flag is on.
This commit is contained in:
@@ -3287,9 +3287,18 @@
|
||||
"opensInANewWindow": {
|
||||
"message": "Opens in a new window"
|
||||
},
|
||||
"rememberThisDeviceToMakeFutureLoginsSeamless": {
|
||||
"message": "Remember this device to make future logins seamless"
|
||||
},
|
||||
"deviceApprovalRequired": {
|
||||
"message": "Device approval required. Select an approval option below:"
|
||||
},
|
||||
"deviceApprovalRequiredV2": {
|
||||
"message": "Device approval required"
|
||||
},
|
||||
"selectAnApprovalOptionBelow": {
|
||||
"message": "Select an approval option below"
|
||||
},
|
||||
"rememberThisDevice": {
|
||||
"message": "Remember this device"
|
||||
},
|
||||
@@ -3363,6 +3372,9 @@
|
||||
"userEmailMissing": {
|
||||
"message": "User email missing"
|
||||
},
|
||||
"activeUserEmailNotFoundLoggingYouOut": {
|
||||
"message": "Active user email not found. Logging you out."
|
||||
},
|
||||
"deviceTrusted": {
|
||||
"message": "Device trusted"
|
||||
},
|
||||
@@ -3799,6 +3811,9 @@
|
||||
"accessing": {
|
||||
"message": "Accessing"
|
||||
},
|
||||
"loggedInExclamation": {
|
||||
"message": "Logged in!"
|
||||
},
|
||||
"passkeyNotCopied": {
|
||||
"message": "Passkey will not be copied"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Router } from "@angular/router";
|
||||
import { MockProxy, mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
|
||||
import { postLogoutMessageListener$ } from "../utils/post-logout-message-listener";
|
||||
|
||||
import { ExtensionLoginDecryptionOptionsService } from "./extension-login-decryption-options.service";
|
||||
|
||||
// Mock the module providing postLogoutMessageListener$
|
||||
jest.mock("../utils/post-logout-message-listener", () => {
|
||||
return {
|
||||
postLogoutMessageListener$: new BehaviorSubject<string>(""), // Replace with mock subject
|
||||
};
|
||||
});
|
||||
|
||||
describe("ExtensionLoginDecryptionOptionsService", () => {
|
||||
let service: ExtensionLoginDecryptionOptionsService;
|
||||
|
||||
let messagingService: MockProxy<MessagingService>;
|
||||
let router: MockProxy<Router>;
|
||||
let postLogoutMessageSubject: BehaviorSubject<string>;
|
||||
|
||||
beforeEach(() => {
|
||||
messagingService = mock<MessagingService>();
|
||||
router = mock<Router>();
|
||||
|
||||
// Cast postLogoutMessageListener$ to BehaviorSubject for dynamic control
|
||||
postLogoutMessageSubject = postLogoutMessageListener$ as BehaviorSubject<string>;
|
||||
|
||||
service = new ExtensionLoginDecryptionOptionsService(messagingService, router);
|
||||
});
|
||||
|
||||
it("should instantiate the service", () => {
|
||||
expect(service).not.toBeFalsy();
|
||||
});
|
||||
|
||||
describe("logOut()", () => {
|
||||
it("should send a logout message", async () => {
|
||||
postLogoutMessageSubject.next("switchAccountFinish");
|
||||
|
||||
await service.logOut();
|
||||
|
||||
expect(messagingService.send).toHaveBeenCalledWith("logout");
|
||||
});
|
||||
|
||||
it("should navigate to root on 'switchAccountFinish'", async () => {
|
||||
postLogoutMessageSubject.next("switchAccountFinish");
|
||||
|
||||
await service.logOut();
|
||||
|
||||
expect(router.navigate).toHaveBeenCalledWith(["/"]);
|
||||
});
|
||||
|
||||
it("should not navigate for 'doneLoggingOut'", async () => {
|
||||
postLogoutMessageSubject.next("doneLoggingOut");
|
||||
|
||||
await service.logOut();
|
||||
|
||||
expect(router.navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import {
|
||||
DefaultLoginDecryptionOptionsService,
|
||||
LoginDecryptionOptionsService,
|
||||
} from "@bitwarden/auth/angular";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
|
||||
import { postLogoutMessageListener$ } from "../utils/post-logout-message-listener";
|
||||
|
||||
export class ExtensionLoginDecryptionOptionsService
|
||||
extends DefaultLoginDecryptionOptionsService
|
||||
implements LoginDecryptionOptionsService
|
||||
{
|
||||
constructor(
|
||||
protected messagingService: MessagingService,
|
||||
private router: Router,
|
||||
) {
|
||||
super(messagingService);
|
||||
}
|
||||
|
||||
override async logOut(): Promise<void> {
|
||||
// start listening for "switchAccountFinish" or "doneLoggingOut"
|
||||
const messagePromise = firstValueFrom(postLogoutMessageListener$);
|
||||
|
||||
super.logOut();
|
||||
|
||||
// wait for messages
|
||||
const command = await messagePromise;
|
||||
|
||||
// doneLoggingOut already has a message handler that will navigate us
|
||||
if (command === "switchAccountFinish") {
|
||||
await this.router.navigate(["/"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
||||
import { BaseLoginDecryptionOptionsComponentV1 } from "@bitwarden/angular/auth/components/base-login-decryption-options-v1.component";
|
||||
|
||||
import { postLogoutMessageListener$ } from "../utils/post-logout-message-listener";
|
||||
|
||||
@Component({
|
||||
selector: "browser-login-decryption-options",
|
||||
templateUrl: "login-decryption-options.component.html",
|
||||
templateUrl: "login-decryption-options-v1.component.html",
|
||||
})
|
||||
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
||||
export class LoginDecryptionOptionsComponentV1 extends BaseLoginDecryptionOptionsComponentV1 {
|
||||
override async createUser(): Promise<void> {
|
||||
try {
|
||||
await super.createUser();
|
||||
@@ -21,7 +21,6 @@ import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh
|
||||
import {
|
||||
AnonLayoutWrapperComponent,
|
||||
AnonLayoutWrapperData,
|
||||
DevicesIcon,
|
||||
LoginComponent,
|
||||
LoginSecondaryContentComponent,
|
||||
LockIcon,
|
||||
@@ -37,6 +36,8 @@ import {
|
||||
SetPasswordJitComponent,
|
||||
UserLockIcon,
|
||||
VaultIcon,
|
||||
LoginDecryptionOptionsComponent,
|
||||
DevicesIcon,
|
||||
} from "@bitwarden/auth/angular";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
|
||||
@@ -51,7 +52,7 @@ import {
|
||||
import { HintComponent } from "../auth/popup/hint.component";
|
||||
import { HomeComponent } from "../auth/popup/home.component";
|
||||
import { LockComponent } from "../auth/popup/lock.component";
|
||||
import { LoginDecryptionOptionsComponent } from "../auth/popup/login-decryption-options/login-decryption-options.component";
|
||||
import { LoginDecryptionOptionsComponentV1 } from "../auth/popup/login-decryption-options/login-decryption-options-v1.component";
|
||||
import { LoginComponentV1 } from "../auth/popup/login-v1.component";
|
||||
import { LoginViaAuthRequestComponentV1 } from "../auth/popup/login-via-auth-request-v1.component";
|
||||
import { RegisterComponent } from "../auth/popup/register.component";
|
||||
@@ -206,12 +207,6 @@ const routes: Routes = [
|
||||
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
||||
data: { state: "2fa-options" } satisfies RouteDataProperties,
|
||||
},
|
||||
{
|
||||
path: "login-initiated",
|
||||
component: LoginDecryptionOptionsComponent,
|
||||
canActivate: [tdeDecryptionRequiredGuard()],
|
||||
data: { state: "login-initiated" } satisfies RouteDataProperties,
|
||||
},
|
||||
{
|
||||
path: "sso",
|
||||
component: SsoComponent,
|
||||
@@ -534,6 +529,23 @@ const routes: Routes = [
|
||||
],
|
||||
},
|
||||
),
|
||||
...unauthUiRefreshSwap(
|
||||
LoginDecryptionOptionsComponentV1,
|
||||
ExtensionAnonLayoutWrapperComponent,
|
||||
{
|
||||
path: "login-initiated",
|
||||
canActivate: [tdeDecryptionRequiredGuard()],
|
||||
data: { state: "login-initiated" } satisfies RouteDataProperties,
|
||||
},
|
||||
{
|
||||
path: "login-initiated",
|
||||
canActivate: [tdeDecryptionRequiredGuard()],
|
||||
data: {
|
||||
pageIcon: DevicesIcon,
|
||||
},
|
||||
children: [{ path: "", component: LoginDecryptionOptionsComponent }],
|
||||
},
|
||||
),
|
||||
{
|
||||
path: "",
|
||||
component: ExtensionAnonLayoutWrapperComponent,
|
||||
|
||||
@@ -24,7 +24,7 @@ import { ExtensionAnonLayoutWrapperComponent } from "../auth/popup/extension-ano
|
||||
import { HintComponent } from "../auth/popup/hint.component";
|
||||
import { HomeComponent } from "../auth/popup/home.component";
|
||||
import { LockComponent } from "../auth/popup/lock.component";
|
||||
import { LoginDecryptionOptionsComponent } from "../auth/popup/login-decryption-options/login-decryption-options.component";
|
||||
import { LoginDecryptionOptionsComponentV1 } from "../auth/popup/login-decryption-options/login-decryption-options-v1.component";
|
||||
import { LoginComponentV1 } from "../auth/popup/login-v1.component";
|
||||
import { LoginViaAuthRequestComponentV1 } from "../auth/popup/login-via-auth-request-v1.component";
|
||||
import { RegisterComponent } from "../auth/popup/register.component";
|
||||
@@ -161,7 +161,7 @@ import "../platform/popup/locales";
|
||||
LockComponent,
|
||||
LoginViaAuthRequestComponentV1,
|
||||
LoginComponentV1,
|
||||
LoginDecryptionOptionsComponent,
|
||||
LoginDecryptionOptionsComponentV1,
|
||||
NotificationsSettingsV1Component,
|
||||
AppearanceComponent,
|
||||
GeneratorComponent,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { APP_INITIALIZER, NgModule, NgZone } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subject, merge, of } from "rxjs";
|
||||
|
||||
import { CollectionService } from "@bitwarden/admin-console/common";
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
AnonLayoutWrapperDataService,
|
||||
LoginComponentService,
|
||||
LockComponentService,
|
||||
LoginDecryptionOptionsService,
|
||||
} from "@bitwarden/auth/angular";
|
||||
import { LockService, LoginEmailService, PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
@@ -115,6 +117,7 @@ import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
import { ForegroundLockService } from "../../auth/popup/accounts/foreground-lock.service";
|
||||
import { ExtensionAnonLayoutWrapperDataService } from "../../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper-data.service";
|
||||
import { ExtensionLoginComponentService } from "../../auth/popup/login/extension-login-component.service";
|
||||
import { ExtensionLoginDecryptionOptionsService } from "../../auth/popup/login-decryption-options/extension-login-decryption-options.service";
|
||||
import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service";
|
||||
import AutofillService from "../../autofill/services/autofill.service";
|
||||
import { InlineMenuFieldQualificationService } from "../../autofill/services/inline-menu-field-qualification.service";
|
||||
@@ -591,6 +594,11 @@ const safeProviders: SafeProvider[] = [
|
||||
useExisting: PopupCompactModeService,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: LoginDecryptionOptionsService,
|
||||
useClass: ExtensionLoginDecryptionOptionsService,
|
||||
deps: [MessagingServiceAbstraction, Router],
|
||||
}),
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
Reference in New Issue
Block a user