diff --git a/apps/browser/src/auth/popup/two-factor-auth-email.component.ts b/apps/browser/src/auth/popup/two-factor-auth-email.component.ts new file mode 100644 index 0000000000..e865435b8b --- /dev/null +++ b/apps/browser/src/auth/popup/two-factor-auth-email.component.ts @@ -0,0 +1,55 @@ +import { DialogModule } from "@angular/cdk/dialog"; +import { CommonModule } from "@angular/common"; +import { Component, inject } from "@angular/core"; +import { ReactiveFormsModule, FormsModule } from "@angular/forms"; + +import { TwoFactorAuthEmailComponent as TwoFactorAuthEmailBaseComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-email.component"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; + +import { AsyncActionsModule } from "../../../../../libs/components/src/async-actions"; +import { ButtonModule } from "../../../../../libs/components/src/button"; +import { DialogService } from "../../../../../libs/components/src/dialog"; +import { FormFieldModule } from "../../../../../libs/components/src/form-field"; +import { LinkModule } from "../../../../../libs/components/src/link"; +import { I18nPipe } from "../../../../../libs/components/src/shared/i18n.pipe"; +import { TypographyModule } from "../../../../../libs/components/src/typography"; +import BrowserPopupUtils from "../../platform/popup/browser-popup-utils"; + +@Component({ + standalone: true, + selector: "app-two-factor-auth-email", + templateUrl: + "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component.html", + imports: [ + CommonModule, + JslibModule, + DialogModule, + ButtonModule, + LinkModule, + TypographyModule, + ReactiveFormsModule, + FormFieldModule, + AsyncActionsModule, + FormsModule, + ], + providers: [I18nPipe], +}) +export class TwoFactorAuthEmailComponent extends TwoFactorAuthEmailBaseComponent { + private dialogService = inject(DialogService); + + async ngOnInit(): Promise { + if (BrowserPopupUtils.inPopup(window)) { + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "warning" }, + content: { key: "popup2faCloseMessage" }, + type: "warning", + }); + if (confirmed) { + await BrowserPopupUtils.openCurrentPagePopout(window); + return; + } + } + + await super.ngOnInit(); + } +} diff --git a/apps/browser/src/auth/popup/two-factor-auth.component.ts b/apps/browser/src/auth/popup/two-factor-auth.component.ts index 67ff0fd285..23251e2d58 100644 --- a/apps/browser/src/auth/popup/two-factor-auth.component.ts +++ b/apps/browser/src/auth/popup/two-factor-auth.component.ts @@ -41,6 +41,8 @@ import { import { BrowserApi } from "../../platform/browser/browser-api"; import BrowserPopupUtils from "../../platform/popup/browser-popup-utils"; +import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component"; + @Component({ standalone: true, templateUrl: @@ -59,6 +61,7 @@ import BrowserPopupUtils from "../../platform/popup/browser-popup-utils"; RouterLink, CheckboxModule, TwoFactorOptionsComponent, + TwoFactorAuthEmailComponent, TwoFactorAuthAuthenticatorComponent, TwoFactorAuthYubikeyComponent, ], diff --git a/apps/desktop/src/auth/two-factor-auth.component.ts b/apps/desktop/src/auth/two-factor-auth.component.ts index 191a88e621..a07509527e 100644 --- a/apps/desktop/src/auth/two-factor-auth.component.ts +++ b/apps/desktop/src/auth/two-factor-auth.component.ts @@ -5,6 +5,7 @@ import { ReactiveFormsModule } from "@angular/forms"; import { RouterLink } from "@angular/router"; import { TwoFactorAuthAuthenticatorComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-authenticator.component"; +import { TwoFactorAuthEmailComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component"; import { TwoFactorAuthYubikeyComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-yubikey.component"; import { TwoFactorAuthComponent as BaseTwoFactorAuthComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component"; import { TwoFactorOptionsComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-options.component"; @@ -35,6 +36,7 @@ import { TypographyModule } from "../../../../libs/components/src/typography"; RouterLink, CheckboxModule, TwoFactorOptionsComponent, + TwoFactorAuthEmailComponent, TwoFactorAuthAuthenticatorComponent, TwoFactorAuthYubikeyComponent, ], diff --git a/apps/web/src/app/auth/two-factor-auth.component.ts b/apps/web/src/app/auth/two-factor-auth.component.ts index 8bdd458ea9..9834529b52 100644 --- a/apps/web/src/app/auth/two-factor-auth.component.ts +++ b/apps/web/src/app/auth/two-factor-auth.component.ts @@ -20,6 +20,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { LinkModule, TypographyModule, CheckboxModule, DialogService } from "@bitwarden/components"; import { TwoFactorAuthAuthenticatorComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-authenticator.component"; +import { TwoFactorAuthEmailComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component"; import { TwoFactorAuthYubikeyComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-yubikey.component"; import { TwoFactorAuthComponent as BaseTwoFactorAuthComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component"; import { TwoFactorOptionsComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-options.component"; @@ -50,6 +51,7 @@ import { FormFieldModule } from "../../../../../libs/components/src/form-field"; RouterLink, CheckboxModule, TwoFactorOptionsComponent, + TwoFactorAuthEmailComponent, TwoFactorAuthAuthenticatorComponent, TwoFactorAuthYubikeyComponent, ], diff --git a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component.html b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component.html new file mode 100644 index 0000000000..c9d0901bca --- /dev/null +++ b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component.html @@ -0,0 +1,19 @@ +

+ {{ "enterVerificationCodeEmail" | i18n: twoFactorEmail }} +

+ + {{ "verificationCode" | i18n }} + + + + {{ "sendVerificationCodeEmailAgain" | i18n }} + + diff --git a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component.ts b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component.ts new file mode 100644 index 0000000000..7ac18bbc96 --- /dev/null +++ b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component.ts @@ -0,0 +1,109 @@ +import { DialogModule } from "@angular/cdk/dialog"; +import { CommonModule } from "@angular/common"; +import { Component, EventEmitter, Output } from "@angular/core"; +import { ReactiveFormsModule, FormsModule } from "@angular/forms"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe"; +import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; +import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; +import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request"; +import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { + ButtonModule, + LinkModule, + TypographyModule, + FormFieldModule, + AsyncActionsModule, +} from "@bitwarden/components"; + +@Component({ + standalone: true, + selector: "app-two-factor-auth-email", + templateUrl: "two-factor-auth-email.component.html", + imports: [ + CommonModule, + JslibModule, + DialogModule, + ButtonModule, + LinkModule, + TypographyModule, + ReactiveFormsModule, + FormFieldModule, + AsyncActionsModule, + FormsModule, + ], + providers: [I18nPipe], +}) +export class TwoFactorAuthEmailComponent { + @Output() token = new EventEmitter(); + + twoFactorEmail: string = null; + emailPromise: Promise; + tokenValue: string = ""; + + constructor( + protected i18nService: I18nService, + protected twoFactorService: TwoFactorService, + protected loginStrategyService: LoginStrategyServiceAbstraction, + protected platformUtilsService: PlatformUtilsService, + protected logService: LogService, + protected apiService: ApiService, + protected appIdService: AppIdService, + ) {} + + async ngOnInit(): Promise { + const providerData = await this.twoFactorService.getProviders().then((providers) => { + return providers.get(TwoFactorProviderType.Email); + }); + this.twoFactorEmail = providerData.Email; + + if ((await this.twoFactorService.getProviders()).size > 1) { + await this.sendEmail(false); + } + } + + async sendEmail(doToast: boolean) { + if (this.emailPromise != null) { + return; + } + + if ((await this.loginStrategyService.getEmail()) == null) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("errorOccurred"), + this.i18nService.t("sessionTimeout"), + ); + return; + } + + try { + const request = new TwoFactorEmailRequest(); + request.email = await this.loginStrategyService.getEmail(); + request.masterPasswordHash = await this.loginStrategyService.getMasterPasswordHash(); + request.ssoEmail2FaSessionToken = + await this.loginStrategyService.getSsoEmail2FaSessionToken(); + request.deviceIdentifier = await this.appIdService.getAppId(); + request.authRequestAccessCode = await this.loginStrategyService.getAccessCode(); + request.authRequestId = await this.loginStrategyService.getAuthRequestId(); + this.emailPromise = this.apiService.postTwoFactorEmail(request); + await this.emailPromise; + if (doToast) { + this.platformUtilsService.showToast( + "success", + null, + this.i18nService.t("verificationCodeEmailSent", this.twoFactorEmail), + ); + } + } catch (e) { + this.logService.error(e); + } + + this.emailPromise = null; + } +} diff --git a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.html b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.html index 1de1561a34..1d29cc5a4f 100644 --- a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.html +++ b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.html @@ -1,5 +1,9 @@
+