[PM-7084] 4/6: Introduce shared email two-factor component (#9770)
* Add shared email two-factor component * Update apps/browser/src/auth/popup/two-factor-auth-email.component.ts Co-authored-by: Jake Fink <jfink@bitwarden.com> --------- Co-authored-by: Jake Fink <jfink@bitwarden.com>
This commit is contained in:
parent
a1667e5603
commit
5a46c7d5cc
|
@ -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<void> {
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
],
|
||||
|
|
|
@ -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,
|
||||
],
|
||||
|
|
|
@ -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,
|
||||
],
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<p bitTypography="body1">
|
||||
{{ "enterVerificationCodeEmail" | i18n: twoFactorEmail }}
|
||||
</p>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "verificationCode" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="text"
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
[(ngModel)]="tokenValue"
|
||||
(input)="token.emit(tokenValue)"
|
||||
/>
|
||||
<bit-hint>
|
||||
<a bitLink href="#" appStopClick (click)="sendEmail(true)">
|
||||
{{ "sendVerificationCodeEmailAgain" | i18n }}
|
||||
</a></bit-hint
|
||||
>
|
||||
</bit-form-field>
|
|
@ -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<string>();
|
||||
|
||||
twoFactorEmail: string = null;
|
||||
emailPromise: Promise<any>;
|
||||
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<void> {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
<form [bitSubmit]="submitForm" [formGroup]="formGroup" autocomplete="off">
|
||||
<div class="tw-min-w-96">
|
||||
<app-two-factor-auth-email
|
||||
(token)="token = $event"
|
||||
*ngIf="selectedProviderType === providerType.Email"
|
||||
/>
|
||||
<app-two-factor-auth-authenticator
|
||||
(token)="token = $event"
|
||||
*ngIf="selectedProviderType === providerType.Authenticator"
|
||||
|
|
|
@ -39,6 +39,7 @@ import {
|
|||
import { CaptchaProtectedComponent } from "../captcha-protected.component";
|
||||
|
||||
import { TwoFactorAuthAuthenticatorComponent } from "./two-factor-auth-authenticator.component";
|
||||
import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component";
|
||||
import { TwoFactorAuthYubikeyComponent } from "./two-factor-auth-yubikey.component";
|
||||
import {
|
||||
TwoFactorOptionsDialogResult,
|
||||
|
@ -60,6 +61,7 @@ import {
|
|||
ButtonModule,
|
||||
TwoFactorOptionsComponent,
|
||||
TwoFactorAuthAuthenticatorComponent,
|
||||
TwoFactorAuthEmailComponent,
|
||||
TwoFactorAuthYubikeyComponent,
|
||||
],
|
||||
providers: [I18nPipe],
|
||||
|
|
Loading…
Reference in New Issue