From 96d60469894f874528f92fb1822d94c9819ff6a2 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Fri, 7 Nov 2025 10:46:55 -0800 Subject: [PATCH] update Desktop VaultV2Component message sending --- .../extension-auth-request-answering.service.ts | 5 +++-- apps/browser/src/popup/app.component.ts | 6 ++++++ apps/desktop/src/app/app.component.ts | 4 ++++ .../desktop-auth-request-answering.service.ts | 7 ++++--- apps/desktop/src/vault/app/vault/vault-v2.component.ts | 7 +++---- .../default-auth-request-answering.service.ts | 6 ++---- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/apps/browser/src/auth/services/auth-request-answering/extension-auth-request-answering.service.ts b/apps/browser/src/auth/services/auth-request-answering/extension-auth-request-answering.service.ts index 485c442c286..175d6e899b0 100644 --- a/apps/browser/src/auth/services/auth-request-answering/extension-auth-request-answering.service.ts +++ b/apps/browser/src/auth/services/auth-request-answering/extension-auth-request-answering.service.ts @@ -50,9 +50,10 @@ export class ExtensionAuthRequestAnsweringService // Always persist the pending marker for this user to global state. await this.pendingAuthRequestsState.add(userId); - const userIsAvailableToViewDialog = await this.userMeetsConditionsToShowApprovalDialog(userId); + const userMeetsConditionsToShowApprovalDialog = + await this.userMeetsConditionsToShowApprovalDialog(userId); - if (userIsAvailableToViewDialog) { + if (userMeetsConditionsToShowApprovalDialog) { // Send message to open dialog immediately for this request this.messagingService.send("openLoginApproval", { // Include the authRequestId so the DeviceManagementComponent can upsert the correct device. diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 1a3c846dcb2..55e0c430ea0 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -200,18 +200,23 @@ export class AppComponent implements OnInit, OnDestroy { if (this.processingPendingAuth) { return; } + this.processingPendingAuth = true; + try { // Always query server for all pending requests and open a dialog for each const pendingList = await firstValueFrom( this.authRequestService.getPendingAuthRequests$(), ); + if (Array.isArray(pendingList) && pendingList.length > 0) { const respondedIds = new Set(); + for (const req of pendingList) { if (req?.id == null) { continue; } + const dialogRef = LoginApprovalDialogComponent.open(this.dialogService, { notificationId: req.id, }); @@ -220,6 +225,7 @@ export class AppComponent implements OnInit, OnDestroy { if (result !== undefined && typeof result === "boolean") { respondedIds.add(req.id); + if (respondedIds.size === pendingList.length && this.activeUserId != null) { await this.pendingAuthRequestsState.clear(this.activeUserId); } diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 0a1782700c5..5907f7bd687 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -516,12 +516,15 @@ export class AppComponent implements OnInit, OnDestroy { const pendingList = await firstValueFrom( this.authRequestService.getPendingAuthRequests$(), ); + if (Array.isArray(pendingList) && pendingList.length > 0) { const respondedIds = new Set(); + for (const req of pendingList) { if (req?.id == null) { continue; } + const dialogRef = LoginApprovalDialogComponent.open(this.dialogService, { notificationId: req.id, }); @@ -530,6 +533,7 @@ export class AppComponent implements OnInit, OnDestroy { if (result !== undefined && typeof result === "boolean") { respondedIds.add(req.id); + if (respondedIds.size === pendingList.length && this.activeUserId != null) { await this.pendingAuthRequestsState.clear(this.activeUserId); } diff --git a/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.ts b/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.ts index 9be2dcf4ffd..1a091b498b0 100644 --- a/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.ts +++ b/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.ts @@ -42,9 +42,10 @@ export class DesktopAuthRequestAnsweringService // Always persist the pending marker for this user to global state. await this.pendingAuthRequestsState.add(userId); - const userIsAvailableToViewDialog = await this.userMeetsConditionsToShowApprovalDialog(userId); + const userMeetsConditionsToShowApprovalDialog = + await this.userMeetsConditionsToShowApprovalDialog(userId); - if (userIsAvailableToViewDialog) { + if (userMeetsConditionsToShowApprovalDialog) { // Send message to open dialog immediately for this request this.messagingService.send("openLoginApproval"); } @@ -56,7 +57,7 @@ export class DesktopAuthRequestAnsweringService // - User does meet conditions, but the Desktop window is not visible // - In this second case, we both send the "openLoginApproval" message (above) AND // also create the system notification to notify the user that the dialog is there. - if (!userIsAvailableToViewDialog || !isWindowVisible) { + if (!userMeetsConditionsToShowApprovalDialog || !isWindowVisible) { const accounts = await firstValueFrom(this.accountService.accounts$); const emailForUser = accounts[userId].email; await ipc.auth.loginRequest( diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index fbf5c6ac632..217104b6bdc 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -16,7 +16,6 @@ import { CollectionService, CollectionView } from "@bitwarden/admin-console/comm import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; -import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -28,7 +27,6 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EventType } from "@bitwarden/common/enums"; 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"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -223,8 +221,6 @@ export class VaultV2Component private collectionService: CollectionService, private organizationService: OrganizationService, private folderService: FolderService, - private configService: ConfigService, - private authRequestService: AuthRequestServiceAbstraction, private cipherArchiveService: CipherArchiveService, private policyService: PolicyService, private archiveCipherUtilitiesService: ArchiveCipherUtilitiesService, @@ -337,6 +333,9 @@ export class VaultV2Component this.searchBarService.setEnabled(true); this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault")); + // If there are pending auth requests for this user, a LoginApprovalDialogComponent will open + this.messagingService.send("openLoginApproval"); + this.activeUserId = await firstValueFrom( this.accountService.activeAccount$.pipe(getUserId), ).catch((): any => null); diff --git a/libs/common/src/auth/services/auth-request-answering/default-auth-request-answering.service.ts b/libs/common/src/auth/services/auth-request-answering/default-auth-request-answering.service.ts index 992e1952d0e..132d43b9a3c 100644 --- a/libs/common/src/auth/services/auth-request-answering/default-auth-request-answering.service.ts +++ b/libs/common/src/auth/services/auth-request-answering/default-auth-request-answering.service.ts @@ -88,7 +88,7 @@ export class DefaultAuthRequestAnsweringService implements AuthRequestAnsweringS } setupUnlockListenersForProcessingAuthRequests(destroy$: Observable): void { - // Trigger processing auth requests when the active user is in an unlocked state. + // When account switching to a user who is Unlocked, process any pending auth requests. this.accountService.activeAccount$ .pipe( map((a) => a?.id), // Extract active userId @@ -97,15 +97,13 @@ export class DefaultAuthRequestAnsweringService implements AuthRequestAnsweringS switchMap((userId) => this.authService.authStatusFor$(userId).pipe(take(1))), // Get current auth status once for new user filter((status) => status === AuthenticationStatus.Unlocked), // Only when the new user is Unlocked tap(() => { - // Trigger processing when switching users while app is visible. void this.processPendingAuthRequests(); }), takeUntil(destroy$), ) .subscribe(); - // When the app is already visible and the active account transitions to Unlocked, process any - // pending auth requests for the active user. The above subscription does not handle this case. + // When the active account transitions TO Unlocked, process any pending auth requests. this.authService.activeAccountStatus$ .pipe( startWith(null as unknown as AuthenticationStatus), // Seed previous value to handle initial emission