From ad43e635b622da8ddb03007957ca11690a58aa6e Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Fri, 17 Oct 2025 16:13:49 -0700 Subject: [PATCH] create DesktopAuthRequestAnsweringService --- .../src/app/services/services.module.ts | 27 ++++-- ...-approval-dialog-component.service.spec.ts | 91 ------------------ ...login-approval-dialog-component.service.ts | 28 ------ .../desktop-auth-request-answering.service.ts | 96 +++++++++++++++++++ .../src/vault/app/vault/vault-v2.component.ts | 16 ++-- ...-approval-dialog-component.service.spec.ts | 30 ------ ...login-approval-dialog-component.service.ts | 16 ---- libs/angular/src/auth/login-approval/index.ts | 2 - ...al-dialog-component.service.abstraction.ts | 9 -- .../login-approval-dialog.component.spec.ts | 5 - .../login-approval-dialog.component.ts | 7 -- .../src/services/jslib-services.module.ts | 7 -- .../default-auth-request-answering.service.ts | 9 +- 13 files changed, 124 insertions(+), 219 deletions(-) delete mode 100644 apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.spec.ts delete mode 100644 apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.ts create mode 100644 apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.ts delete mode 100644 libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.spec.ts delete mode 100644 libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.ts delete mode 100644 libs/angular/src/auth/login-approval/login-approval-dialog-component.service.abstraction.ts diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 9f2bb1acc90..7f1f8362b5e 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -5,7 +5,6 @@ import { Router } from "@angular/router"; import { Subject, merge } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; -import { LoginApprovalDialogComponentServiceAbstraction } from "@bitwarden/angular/auth/login-approval"; import { SetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction"; import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { @@ -45,12 +44,14 @@ import { AccountService, AccountService as AccountServiceAbstraction, } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; import { AuthService, AuthService as AuthServiceAbstraction, } from "@bitwarden/common/auth/abstractions/auth.service"; import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { PendingAuthRequestsStateService } from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; import { ClientType } from "@bitwarden/common/enums"; @@ -59,7 +60,10 @@ import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { + InternalMasterPasswordServiceAbstraction, + MasterPasswordServiceAbstraction, +} from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { DefaultProcessReloadService } from "@bitwarden/common/key-management/services/default-process-reload.service"; import { @@ -110,8 +114,8 @@ import { LockComponentService } from "@bitwarden/key-management-ui"; import { SerializedMemoryStorageService } from "@bitwarden/storage-core"; import { DefaultSshImportPromptService, SshImportPromptService } from "@bitwarden/vault"; -import { DesktopLoginApprovalDialogComponentService } from "../../auth/login/desktop-login-approval-dialog-component.service"; import { DesktopLoginComponentService } from "../../auth/login/desktop-login-component.service"; +import { DesktopAuthRequestAnsweringService } from "../../auth/services/auth-request-answering/desktop-auth-request-answering.service"; import { DesktopTwoFactorAuthDuoComponentService } from "../../auth/services/desktop-two-factor-auth-duo-component.service"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopAutofillService } from "../../autofill/services/desktop-autofill.service"; @@ -447,11 +451,6 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultSsoComponentService, deps: [], }), - safeProvider({ - provide: LoginApprovalDialogComponentServiceAbstraction, - useClass: DesktopLoginApprovalDialogComponentService, - deps: [I18nServiceAbstraction], - }), safeProvider({ provide: SshImportPromptService, useClass: DefaultSshImportPromptService, @@ -476,6 +475,18 @@ const safeProviders: SafeProvider[] = [ useClass: DesktopAutotypeDefaultSettingPolicy, deps: [AccountServiceAbstraction, AuthServiceAbstraction, InternalPolicyService, ConfigService], }), + safeProvider({ + provide: AuthRequestAnsweringService, + useClass: DesktopAuthRequestAnsweringService, + deps: [ + AccountServiceAbstraction, + AuthService, + MasterPasswordServiceAbstraction, + MessagingServiceAbstraction, + PendingAuthRequestsStateService, + I18nServiceAbstraction, + ], + }), ]; @NgModule({ diff --git a/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.spec.ts b/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.spec.ts deleted file mode 100644 index 2ae584d7e7f..00000000000 --- a/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { TestBed } from "@angular/core/testing"; -import { mock, MockProxy } from "jest-mock-extended"; -import { Subject } from "rxjs"; - -import { LoginApprovalDialogComponent } from "@bitwarden/angular/auth/login-approval"; -import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; - -import { DesktopLoginApprovalDialogComponentService } from "./desktop-login-approval-dialog-component.service"; - -describe("DesktopLoginApprovalDialogComponentService", () => { - let service: DesktopLoginApprovalDialogComponentService; - let i18nService: MockProxy; - let originalIpc: any; - - beforeEach(() => { - originalIpc = (global as any).ipc; - (global as any).ipc = { - auth: { - loginRequest: jest.fn(), - }, - platform: { - isWindowVisible: jest.fn(), - }, - }; - - i18nService = mock({ - t: jest.fn(), - userSetLocale$: new Subject(), - locale$: new Subject(), - }); - - TestBed.configureTestingModule({ - providers: [ - DesktopLoginApprovalDialogComponentService, - { provide: I18nServiceAbstraction, useValue: i18nService }, - ], - }); - - service = TestBed.inject(DesktopLoginApprovalDialogComponentService); - }); - - afterEach(() => { - jest.clearAllMocks(); - (global as any).ipc = originalIpc; - }); - - it("is created successfully", () => { - expect(service).toBeTruthy(); - }); - - it("calls ipc.auth.loginRequest with correct parameters when window is not visible", async () => { - const title = "Log in requested"; - const email = "test@bitwarden.com"; - const message = `Confirm access attempt for ${email}`; - const closeText = "Close"; - - const loginApprovalDialogComponent = { email } as LoginApprovalDialogComponent; - i18nService.t.mockImplementation((key: string) => { - switch (key) { - case "accountAccessRequested": - return title; - case "confirmAccessAttempt": - return message; - case "close": - return closeText; - default: - return ""; - } - }); - - jest.spyOn(ipc.platform, "isWindowVisible").mockResolvedValue(false); - jest.spyOn(ipc.auth, "loginRequest").mockResolvedValue(); - - await service.showLoginRequestedAlertIfWindowNotVisible(loginApprovalDialogComponent.email); - - expect(ipc.auth.loginRequest).toHaveBeenCalledWith(title, message, closeText); - }); - - it("does not call ipc.auth.loginRequest when window is visible", async () => { - const loginApprovalDialogComponent = { - email: "test@bitwarden.com", - } as LoginApprovalDialogComponent; - - jest.spyOn(ipc.platform, "isWindowVisible").mockResolvedValue(true); - jest.spyOn(ipc.auth, "loginRequest"); - - await service.showLoginRequestedAlertIfWindowNotVisible(loginApprovalDialogComponent.email); - - expect(ipc.auth.loginRequest).not.toHaveBeenCalled(); - }); -}); diff --git a/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.ts b/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.ts deleted file mode 100644 index 9c48f71990a..00000000000 --- a/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { - DefaultLoginApprovalDialogComponentService, - LoginApprovalDialogComponentServiceAbstraction, -} from "@bitwarden/angular/auth/login-approval"; -import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; - -@Injectable() -export class DesktopLoginApprovalDialogComponentService - extends DefaultLoginApprovalDialogComponentService - implements LoginApprovalDialogComponentServiceAbstraction -{ - constructor(private i18nService: I18nServiceAbstraction) { - super(); - } - - async showLoginRequestedAlertIfWindowNotVisible(email?: string): Promise { - const isVisible = await ipc.platform.isWindowVisible(); - if (!isVisible) { - await ipc.auth.loginRequest( - this.i18nService.t("accountAccessRequested"), - this.i18nService.t("confirmAccessAttempt", email), - this.i18nService.t("close"), - ); - } - } -} 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 new file mode 100644 index 00000000000..47aca132561 --- /dev/null +++ b/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.ts @@ -0,0 +1,96 @@ +import { firstValueFrom } from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { DefaultAuthRequestAnsweringService } from "@bitwarden/common/auth/services/auth-request-answering/default-auth-request-answering.service"; +import { + PendingAuthRequestsStateService, + PendingAuthUserMarker, +} from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state"; +import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { SystemNotificationEvent } from "@bitwarden/common/platform/system-notifications/system-notifications.service"; +import { UserId } from "@bitwarden/user-core"; + +export class DesktopAuthRequestAnsweringService + extends DefaultAuthRequestAnsweringService + implements AuthRequestAnsweringService +{ + constructor( + protected readonly accountService: AccountService, + protected readonly authService: AuthService, + protected readonly masterPasswordService: MasterPasswordServiceAbstraction, + protected readonly messagingService: MessagingService, + protected readonly pendingAuthRequestsState: PendingAuthRequestsStateService, + private readonly i18nService: I18nService, + ) { + super( + accountService, + authService, + masterPasswordService, + messagingService, + pendingAuthRequestsState, + ); + } + + override async receivedPendingAuthRequest(userId: UserId): Promise { + // Always persist the pending marker for this user to global state. + await this.pendingAuthRequestsState.add(userId); + + const userIsAvailableToViewDialog = await this.userMeetsConditionsToShowApprovalDialog(userId); + + if (userIsAvailableToViewDialog) { + // Send message to open dialog immediately for this request + this.messagingService.send("openLoginApproval"); + } else { + // Create a system notification + const accounts = await firstValueFrom(this.accountService.accounts$); + const emailForUser = accounts[userId].email; + await ipc.auth.loginRequest( + this.i18nService.t("accountAccessRequested"), + this.i18nService.t("confirmAccessAttempt", emailForUser), + this.i18nService.t("close"), + ); + } + } + + async userMeetsConditionsToShowApprovalDialog(userId: UserId): Promise { + const meetsBasicConditions = await super.userMeetsConditionsToShowApprovalDialog(userId); + + // To show an approval dialog immediately on Desktop, the window must be open. + const isWindowVisible = await ipc.platform.isWindowVisible(); + const meetsExtensionConditions = meetsBasicConditions && isWindowVisible; + + return meetsExtensionConditions; + } + + async handleAuthRequestNotificationClicked(event: SystemNotificationEvent) { + throw new Error("handleAuthRequestNotificationClicked() not implemented for this client"); + } + + async processPendingAuthRequests(): Promise { + // Prune any stale pending requests (older than 15 minutes) + // This comes from GlobalSettings.cs + // public TimeSpan UserRequestExpiration { get; set; } = TimeSpan.FromMinutes(15); + const fifteenMinutesMs = 15 * 60 * 1000; + + await this.pendingAuthRequestsState.pruneOlderThan(fifteenMinutesMs); + + const pendingAuthRequestsInState: PendingAuthUserMarker[] = + (await firstValueFrom(this.pendingAuthRequestsState.getAll$())) ?? []; + + if (pendingAuthRequestsInState.length > 0) { + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + const pendingAuthRequestsForActiveUser = pendingAuthRequestsInState.some( + (e) => e.userId === activeUserId, + ); + + if (pendingAuthRequestsForActiveUser) { + this.messagingService.send("openLoginApproval"); + } + } + } +} 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 6dda97807be..13830edc4bf 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -320,14 +320,14 @@ export class VaultV2Component this.searchBarService.setEnabled(true); this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault")); - const authRequests = await firstValueFrom( - this.authRequestService.getLatestPendingAuthRequest$()!, - ); - if (authRequests != null) { - this.messagingService.send("openLoginApproval", { - notificationId: authRequests.id, - }); - } + // const authRequests = await firstValueFrom( + // this.authRequestService.getLatestPendingAuthRequest$()!, + // ); + // if (authRequests != null) { + // this.messagingService.send("openLoginApproval", { + // notificationId: authRequests.id, + // }); + // } this.activeUserId = await firstValueFrom( this.accountService.activeAccount$.pipe(getUserId), diff --git a/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.spec.ts b/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.spec.ts deleted file mode 100644 index 018b1ce2547..00000000000 --- a/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { TestBed } from "@angular/core/testing"; - -import { DefaultLoginApprovalDialogComponentService } from "./default-login-approval-dialog-component.service"; -import { LoginApprovalDialogComponent } from "./login-approval-dialog.component"; - -describe("DefaultLoginApprovalDialogComponentService", () => { - let service: DefaultLoginApprovalDialogComponentService; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [DefaultLoginApprovalDialogComponentService], - }); - - service = TestBed.inject(DefaultLoginApprovalDialogComponentService); - }); - - it("is created successfully", () => { - expect(service).toBeTruthy(); - }); - - it("has showLoginRequestedAlertIfWindowNotVisible method that is a no-op", async () => { - const loginApprovalDialogComponent = {} as LoginApprovalDialogComponent; - - const result = await service.showLoginRequestedAlertIfWindowNotVisible( - loginApprovalDialogComponent.email, - ); - - expect(result).toBeUndefined(); - }); -}); diff --git a/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.ts b/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.ts deleted file mode 100644 index 5fefd3c3abb..00000000000 --- a/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { LoginApprovalDialogComponentServiceAbstraction } from "./login-approval-dialog-component.service.abstraction"; - -/** - * Default implementation of the LoginApprovalDialogComponentServiceAbstraction. - */ -export class DefaultLoginApprovalDialogComponentService - implements LoginApprovalDialogComponentServiceAbstraction -{ - /** - * No-op implementation of the showLoginRequestedAlertIfWindowNotVisible method. - * @returns - */ - async showLoginRequestedAlertIfWindowNotVisible(email?: string): Promise { - return; - } -} diff --git a/libs/angular/src/auth/login-approval/index.ts b/libs/angular/src/auth/login-approval/index.ts index 7b34b17d56b..21cde4df742 100644 --- a/libs/angular/src/auth/login-approval/index.ts +++ b/libs/angular/src/auth/login-approval/index.ts @@ -1,3 +1 @@ export * from "./login-approval-dialog.component"; -export * from "./login-approval-dialog-component.service.abstraction"; -export * from "./default-login-approval-dialog-component.service"; diff --git a/libs/angular/src/auth/login-approval/login-approval-dialog-component.service.abstraction.ts b/libs/angular/src/auth/login-approval/login-approval-dialog-component.service.abstraction.ts deleted file mode 100644 index f29311402a7..00000000000 --- a/libs/angular/src/auth/login-approval/login-approval-dialog-component.service.abstraction.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Abstraction for the LoginApprovalDialogComponent service. - */ -export abstract class LoginApprovalDialogComponentServiceAbstraction { - /** - * Shows a login requested alert if the window is not visible. - */ - abstract showLoginRequestedAlertIfWindowNotVisible: (email?: string) => Promise; -} diff --git a/libs/angular/src/auth/login-approval/login-approval-dialog.component.spec.ts b/libs/angular/src/auth/login-approval/login-approval-dialog.component.spec.ts index b21264eb7c8..d17fefaca4f 100644 --- a/libs/angular/src/auth/login-approval/login-approval-dialog.component.spec.ts +++ b/libs/angular/src/auth/login-approval/login-approval-dialog.component.spec.ts @@ -15,7 +15,6 @@ import { UserId } from "@bitwarden/common/types/guid"; import { DialogRef, DIALOG_DATA, ToastService } from "@bitwarden/components"; import { LogService } from "@bitwarden/logging"; -import { LoginApprovalDialogComponentServiceAbstraction } from "./login-approval-dialog-component.service.abstraction"; import { LoginApprovalDialogComponent } from "./login-approval-dialog.component"; describe("LoginApprovalDialogComponent", () => { @@ -67,10 +66,6 @@ describe("LoginApprovalDialogComponent", () => { { provide: LogService, useValue: logService }, { provide: ToastService, useValue: toastService }, { provide: ValidationService, useValue: validationService }, - { - provide: LoginApprovalDialogComponentServiceAbstraction, - useValue: mock(), - }, ], }).compileComponents(); diff --git a/libs/angular/src/auth/login-approval/login-approval-dialog.component.ts b/libs/angular/src/auth/login-approval/login-approval-dialog.component.ts index 19dc3f519c6..1a9070c3c3f 100644 --- a/libs/angular/src/auth/login-approval/login-approval-dialog.component.ts +++ b/libs/angular/src/auth/login-approval/login-approval-dialog.component.ts @@ -24,8 +24,6 @@ import { } from "@bitwarden/components"; import { LogService } from "@bitwarden/logging"; -import { LoginApprovalDialogComponentServiceAbstraction } from "./login-approval-dialog-component.service.abstraction"; - const RequestTimeOut = 60000 * 15; // 15 Minutes const RequestTimeUpdate = 60000 * 5; // 5 Minutes @@ -55,7 +53,6 @@ export class LoginApprovalDialogComponent implements OnInit, OnDestroy { private devicesService: DevicesServiceAbstraction, private dialogRef: DialogRef, private i18nService: I18nService, - private loginApprovalDialogComponentService: LoginApprovalDialogComponentServiceAbstraction, private logService: LogService, private toastService: ToastService, private validationService: ValidationService, @@ -111,10 +108,6 @@ export class LoginApprovalDialogComponent implements OnInit, OnDestroy { this.updateTimeText(); }, RequestTimeUpdate); - await this.loginApprovalDialogComponentService.showLoginRequestedAlertIfWindowNotVisible( - this.email, - ); - this.loading = false; } diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 068b318ce2e..01163b72e37 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -368,8 +368,6 @@ import { VaultExportServiceAbstraction, } from "@bitwarden/vault-export-core"; -import { DefaultLoginApprovalDialogComponentService } from "../auth/login-approval/default-login-approval-dialog-component.service"; -import { LoginApprovalDialogComponentServiceAbstraction } from "../auth/login-approval/login-approval-dialog-component.service.abstraction"; import { DefaultSetInitialPasswordService } from "../auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; import { SetInitialPasswordService } from "../auth/password-management/set-initial-password/set-initial-password.service.abstraction"; import { DeviceTrustToastService as DeviceTrustToastServiceAbstraction } from "../auth/services/device-trust-toast.service.abstraction"; @@ -1581,11 +1579,6 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultSendPasswordService, deps: [CryptoFunctionServiceAbstraction], }), - safeProvider({ - provide: LoginApprovalDialogComponentServiceAbstraction, - useClass: DefaultLoginApprovalDialogComponentService, - deps: [], - }), safeProvider({ provide: LoginDecryptionOptionsService, useClass: DefaultLoginDecryptionOptionsService, 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 026efe6dae4..99c6eb3e6f7 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 @@ -27,14 +27,7 @@ export class DefaultAuthRequestAnsweringService implements AuthRequestAnsweringS ) {} async receivedPendingAuthRequest(userId: UserId): Promise { - // Always persist the pending marker for this user to global state. - await this.pendingAuthRequestsState.add(userId); - - const userIsAvailableToViewDialog = await this.userMeetsConditionsToShowApprovalDialog(userId); - if (userIsAvailableToViewDialog) { - // Send message to open dialog immediately for this request - this.messagingService.send("openLoginApproval"); - } + throw new Error("receivedPendingAuthRequest() not implemented for this client"); } async userMeetsConditionsToShowApprovalDialog(userId: UserId): Promise {