1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

Auth/PM-12077 - Web Process Reload (#11781)

* PM-12077 - Initial work on web process reload - more testing required.

* PM-12077 - Clarify comment

* PM-12077 - Improving UX of logout with process reload.

* PM-12077 - Final tweaks for process reload

* PM-12077 - Remove no longer accurate comment.

* PM-12077 - Per PR feedback, clean up logout reason

---------

Co-authored-by: Matt Bishop <mbishop@bitwarden.com>
This commit is contained in:
Jared Snider
2024-11-20 13:54:27 -05:00
committed by GitHub
parent 365c7dd65e
commit d1499da793
9 changed files with 77 additions and 41 deletions

View File

@@ -6,7 +6,6 @@ import * as jq from "jquery";
import { Subject, filter, firstValueFrom, map, takeUntil, timeout, catchError, of } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
import { LogoutReason } from "@bitwarden/auth/common";
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
@@ -17,6 +16,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -28,7 +28,7 @@ import { StateEventRunnerService } from "@bitwarden/common/platform/state";
import { SyncService } from "@bitwarden/common/platform/sync";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { DialogService, ToastOptions, ToastService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { KeyService, BiometricStateService } from "@bitwarden/key-management";
@@ -60,6 +60,8 @@ export class AppComponent implements OnDestroy, OnInit {
private isIdle = false;
private destroy$ = new Subject<void>();
loading = false;
constructor(
@Inject(DOCUMENT) private document: Document,
private broadcasterService: BroadcasterService,
@@ -91,6 +93,7 @@ export class AppComponent implements OnDestroy, OnInit {
private accountService: AccountService,
private logService: LogService,
private sdkService: SdkService,
private processReloadService: ProcessReloadServiceAbstraction,
) {
if (flagEnabled("sdk")) {
// Warn if the SDK for some reason can't be initialized
@@ -161,7 +164,8 @@ export class AppComponent implements OnDestroy, OnInit {
this.router.navigate(["/"]);
break;
case "logout":
await this.logOut(message.logoutReason, message.redirect);
// note: the message.logoutReason isn't consumed anymore because of the process reload clearing any toasts.
await this.logOut(message.redirect);
break;
case "lockVault":
await this.vaultTimeoutService.lock();
@@ -170,9 +174,8 @@ export class AppComponent implements OnDestroy, OnInit {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.notificationsService.updateConnection(false);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["lock"]);
await this.processReloadService.startProcessReload(this.authService);
break;
case "lockedUrl":
break;
@@ -272,33 +275,16 @@ export class AppComponent implements OnDestroy, OnInit {
this.destroy$.complete();
}
private async displayLogoutReason(logoutReason: LogoutReason) {
let toastOptions: ToastOptions;
switch (logoutReason) {
case "invalidSecurityStamp":
case "sessionExpired": {
toastOptions = {
variant: "warning",
title: this.i18nService.t("loggedOut"),
message: this.i18nService.t("loginExpired"),
};
break;
}
default: {
toastOptions = {
variant: "info",
title: this.i18nService.t("loggedOut"),
message: this.i18nService.t("loggedOutDesc"),
};
break;
}
}
private async logOut(redirect = true) {
// Ensure the loading state is applied before proceeding to avoid a flash
// of the login screen before the process reload fires.
this.ngZone.run(() => {
this.loading = true;
document.body.classList.add("layout_frontend");
});
this.toastService.showToast(toastOptions);
}
private async logOut(logoutReason: LogoutReason, redirect = true) {
await this.displayLogoutReason(logoutReason);
// Note: we don't display a toast logout reason anymore as the process reload
// will prevent any toasts from being displayed long enough to be read
await this.eventUploadService.uploadEvents();
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)));
@@ -334,10 +320,14 @@ export class AppComponent implements OnDestroy, OnInit {
await logoutPromise;
if (redirect) {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["/"]);
await this.router.navigate(["/"]);
}
await this.processReloadService.startProcessReload(this.authService);
// Normally we would need to reset the loading state to false or remove the layout_frontend
// class from the body here, but the process reload completely reloads the app so
// it handles it.
}, userId);
}