diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 63075053e3..bf29ff2fd2 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3045,6 +3045,9 @@ } } }, + "duoHealthCheckResultsInNullAuthUrlError": { + "message": "Error connecting with the Duo service. Use a different two-step login method or contact Duo for assistance." + }, "launchDuoAndFollowStepsToFinishLoggingIn": { "message": "Launch Duo and follow the steps to finish logging in." }, diff --git a/apps/browser/src/auth/popup/two-factor.component.ts b/apps/browser/src/auth/popup/two-factor.component.ts index 98363bc93c..f3c44ca9ca 100644 --- a/apps/browser/src/auth/popup/two-factor.component.ts +++ b/apps/browser/src/auth/popup/two-factor.component.ts @@ -25,7 +25,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { BrowserApi } from "../../platform/browser/browser-api"; import { ZonedMessageListenerService } from "../../platform/browser/zoned-message-listener.service"; @@ -62,6 +62,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { private dialogService: DialogService, masterPasswordService: InternalMasterPasswordServiceAbstraction, accountService: AccountService, + toastService: ToastService, @Inject(WINDOW) protected win: Window, private browserMessagingApi: ZonedMessageListenerService, ) { @@ -84,6 +85,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { configService, masterPasswordService, accountService, + toastService, ); super.onSuccessfulLogin = async () => { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. @@ -226,6 +228,15 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { } override async launchDuoFrameless() { + if (this.duoFramelessUrl === null) { + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("duoHealthCheckResultsInNullAuthUrlError"), + }); + return; + } + const duoHandOffMessage = { title: this.i18nService.t("youSuccessfullyLoggedIn"), message: this.i18nService.t("youMayCloseThisWindow"), diff --git a/apps/desktop/src/auth/two-factor.component.ts b/apps/desktop/src/auth/two-factor.component.ts index d1b84c1fa0..3f5e8aee19 100644 --- a/apps/desktop/src/auth/two-factor.component.ts +++ b/apps/desktop/src/auth/two-factor.component.ts @@ -25,6 +25,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { ToastService } from "@bitwarden/components"; import { TwoFactorOptionsComponent } from "./two-factor-options.component"; @@ -64,6 +65,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { configService: ConfigService, masterPasswordService: InternalMasterPasswordServiceAbstraction, accountService: AccountService, + toastService: ToastService, @Inject(WINDOW) protected win: Window, ) { super( @@ -85,6 +87,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { configService, masterPasswordService, accountService, + toastService, ); super.onSuccessfulLogin = async () => { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. @@ -149,6 +152,15 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { } override async launchDuoFrameless() { + if (this.duoFramelessUrl === null) { + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("duoHealthCheckResultsInNullAuthUrlError"), + }); + return; + } + const duoHandOffMessage = { title: this.i18nService.t("youSuccessfullyLoggedIn"), message: this.i18nService.t("youMayCloseThisWindow"), diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 5154339482..72d17baa14 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -2778,6 +2778,9 @@ } } }, + "duoHealthCheckResultsInNullAuthUrlError": { + "message": "Error connecting with the Duo service. Use a different two-step login method or contact Duo for assistance." + }, "launchDuoAndFollowStepsToFinishLoggingIn": { "message": "Launch Duo and follow the steps to finish logging in." }, diff --git a/apps/web/src/app/auth/two-factor.component.ts b/apps/web/src/app/auth/two-factor.component.ts index b3241a9242..528ce6fda3 100644 --- a/apps/web/src/app/auth/two-factor.component.ts +++ b/apps/web/src/app/auth/two-factor.component.ts @@ -23,7 +23,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { TwoFactorOptionsDialogResult, @@ -69,6 +69,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent implements OnDest configService: ConfigService, masterPasswordService: InternalMasterPasswordServiceAbstraction, accountService: AccountService, + toastService: ToastService, private formBuilder: FormBuilder, @Inject(WINDOW) protected win: Window, ) { @@ -91,6 +92,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent implements OnDest configService, masterPasswordService, accountService, + toastService, ); this.onSuccessfulLoginNavigate = this.goAfterLogIn; } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 0f9e334fea..b99f065746 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -6210,6 +6210,9 @@ } } }, + "duoHealthCheckResultsInNullAuthUrlError": { + "message": "Error connecting with the Duo service. Use a different two-step login method or contact Duo for assistance." + }, "launchDuoAndFollowStepsToFinishLoggingIn": { "message": "Launch Duo and follow the steps to finish logging in." }, diff --git a/libs/angular/src/auth/components/two-factor.component.spec.ts b/libs/angular/src/auth/components/two-factor.component.spec.ts index 0eb248f6d9..3325f3bc32 100644 --- a/libs/angular/src/auth/components/two-factor.component.spec.ts +++ b/libs/angular/src/auth/components/two-factor.component.spec.ts @@ -32,6 +32,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { UserId } from "@bitwarden/common/types/guid"; +import { ToastService } from "@bitwarden/components"; import { TwoFactorComponent } from "./two-factor.component"; @@ -71,6 +72,7 @@ describe("TwoFactorComponent", () => { let mockConfigService: MockProxy; let mockMasterPasswordService: FakeMasterPasswordService; let mockAccountService: FakeAccountService; + let mockToastService: MockProxy; let mockUserDecryptionOpts: { noMasterPassword: UserDecryptionOptions; @@ -102,6 +104,7 @@ describe("TwoFactorComponent", () => { mockSsoLoginService = mock(); mockConfigService = mock(); mockAccountService = mockAccountServiceWith(userId); + mockToastService = mock(); mockMasterPasswordService = new FakeMasterPasswordService(); mockUserDecryptionOpts = { @@ -182,6 +185,7 @@ describe("TwoFactorComponent", () => { { provide: ConfigService, useValue: mockConfigService }, { provide: InternalMasterPasswordServiceAbstraction, useValue: mockMasterPasswordService }, { provide: AccountService, useValue: mockAccountService }, + { provide: ToastService, useValue: mockToastService }, ], }); diff --git a/libs/angular/src/auth/components/two-factor.component.ts b/libs/angular/src/auth/components/two-factor.component.ts index d08e9a0a2e..8c849db6c6 100644 --- a/libs/angular/src/auth/components/two-factor.component.ts +++ b/libs/angular/src/auth/components/two-factor.component.ts @@ -32,6 +32,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { ToastService } from "@bitwarden/components"; import { CaptchaProtectedComponent } from "./captcha-protected.component"; @@ -94,6 +95,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI protected configService: ConfigService, protected masterPasswordService: InternalMasterPasswordServiceAbstraction, protected accountService: AccountService, + protected toastService: ToastService, ) { super(environmentService, i18nService, platformUtilsService); this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win); @@ -474,6 +476,15 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI } async launchDuoFrameless() { + if (this.duoFramelessUrl === null) { + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("duoHealthCheckResultsInNullAuthUrlError"), + }); + return; + } + this.platformUtilsService.launchUri(this.duoFramelessUrl); } }