PM-2055 Update Two Factor Authenticator Dialog (#8972)
* PM-2055 Update Two Factor Authenticator Dialog * PM-2055 Added close to disable two factor * PM-2055 Added a event emitter to capture enabled status
This commit is contained in:
parent
a1442194ae
commit
6fadee7cb4
|
@ -1,109 +1,80 @@
|
|||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="2faAuthenticatorTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title" id="2faAuthenticatorTitle">
|
||||
{{ "twoStepLogin" | i18n }}
|
||||
<small>{{ "authenticatorAppTitle" | i18n }}</small>
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
appA11yTitle="{{ 'close' | i18n }}"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
ngNativeValidate
|
||||
*ngIf="authed"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<ng-container *ngIf="!enabled">
|
||||
<img class="float-right mfaType0" alt="Authenticator app logo" />
|
||||
<p>{{ "twoStepAuthenticatorDesc" | i18n }}</p>
|
||||
<p>
|
||||
<strong>1. {{ "twoStepAuthenticatorDownloadApp" | i18n }}</strong>
|
||||
</p>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="enabled">
|
||||
<app-callout type="success" title="{{ 'enabled' | i18n }}" icon="bwi bwi-check-circle">
|
||||
<p>{{ "twoStepLoginProviderEnabled" | i18n }}</p>
|
||||
{{ "twoStepAuthenticatorReaddDesc" | i18n }}
|
||||
</app-callout>
|
||||
<img class="float-right mfaType0" alt="Authenticator app logo" />
|
||||
<p>{{ "twoStepAuthenticatorNeedApp" | i18n }}</p>
|
||||
</ng-container>
|
||||
<ul class="bwi-ul">
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-apple"></i>{{ "iosDevices" | i18n }}:
|
||||
<a
|
||||
href="https://itunes.apple.com/us/app/authy/id494168017?mt=8"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Authy</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-android"></i>{{ "androidDevices" | i18n }}:
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=com.authy.authy"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Authy</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-windows"></i>{{ "windowsDevices" | i18n }}:
|
||||
<a
|
||||
href="https://www.microsoft.com/p/authenticator/9wzdncrfj3rj"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Microsoft Authenticator</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<p>{{ "twoStepAuthenticatorAppsRecommended" | i18n }}</p>
|
||||
<p *ngIf="!enabled">
|
||||
<strong>2. {{ "twoStepAuthenticatorScanCode" | i18n }}</strong>
|
||||
</p>
|
||||
<hr *ngIf="enabled" />
|
||||
<p class="text-center" [ngClass]="{ 'mb-0': enabled }">
|
||||
<canvas id="qr"></canvas><br />
|
||||
<code appA11yTitle="{{ 'key' | i18n }}">{{ key }}</code>
|
||||
</p>
|
||||
<ng-container *ngIf="!enabled">
|
||||
<label for="token">3. {{ "twoStepAuthenticatorEnterCode" | i18n }}</label>
|
||||
<input
|
||||
id="token"
|
||||
type="text"
|
||||
name="Token"
|
||||
class="form-control"
|
||||
[(ngModel)]="token"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span *ngIf="!enabled">{{ "enable" | i18n }}</span>
|
||||
<span *ngIf="enabled">{{ "disable" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{ "close" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form *ngIf="authed" [formGroup]="formGroup" [bitSubmit]="submit">
|
||||
<bit-dialog dialogSize="default">
|
||||
<span bitDialogTitle
|
||||
>{{ "twoStepLogin" | i18n }}
|
||||
<span bitTypography="body1">{{ "authenticatorAppTitle" | i18n }}</span>
|
||||
</span>
|
||||
<ng-container bitDialogContent>
|
||||
<ng-container *ngIf="!enabled">
|
||||
<img class="float-right mfaType0" alt="Authenticator app logo" />
|
||||
<p bitTypography="body1">{{ "twoStepAuthenticatorDesc" | i18n }}</p>
|
||||
<p bitTypography="body1" class="tw-font-bold">
|
||||
1. {{ "twoStepAuthenticatorDownloadApp" | i18n }}
|
||||
</p>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="enabled">
|
||||
<app-callout type="success" title="{{ 'enabled' | i18n }}" icon="bwi bwi-check-circle">
|
||||
<p bitTypography="body1">{{ "twoStepLoginProviderEnabled" | i18n }}</p>
|
||||
{{ "twoStepAuthenticatorReaddDesc" | i18n }}
|
||||
</app-callout>
|
||||
<img class="float-right mfaType0" alt="Authenticator app logo" />
|
||||
<p bitTypography="body1">{{ "twoStepAuthenticatorNeedApp" | i18n }}</p>
|
||||
</ng-container>
|
||||
<ul class="bwi-ul">
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-apple"></i>{{ "iosDevices" | i18n }}:
|
||||
<a
|
||||
bitLink
|
||||
href="https://itunes.apple.com/us/app/authy/id494168017?mt=8"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Authy</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-android"></i>{{ "androidDevices" | i18n }}:
|
||||
<a
|
||||
bitLink
|
||||
href="https://play.google.com/store/apps/details?id=com.authy.authy"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Authy</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-li bwi-windows"></i>{{ "windowsDevices" | i18n }}:
|
||||
<a
|
||||
bitLink
|
||||
href="https://www.microsoft.com/p/authenticator/9wzdncrfj3rj"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Microsoft Authenticator</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<p bitTypography="body1">{{ "twoStepAuthenticatorAppsRecommended" | i18n }}</p>
|
||||
<p *ngIf="!enabled" bitTypography="body1" class="tw-font-bold">
|
||||
2. {{ "twoStepAuthenticatorScanCode" | i18n }}
|
||||
</p>
|
||||
<hr *ngIf="enabled" />
|
||||
<p class="text-center" [ngClass]="{ 'mb-0': enabled }">
|
||||
<canvas id="qr"></canvas><br />
|
||||
<code appA11yTitle="{{ 'key' | i18n }}">{{ key }}</code>
|
||||
</p>
|
||||
<ng-container *ngIf="!enabled">
|
||||
<bit-form-field>
|
||||
<bit-label>3. {{ "twoStepAuthenticatorEnterCode" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="token" appInputVerbatim />
|
||||
</bit-form-field>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton bitFormButton type="submit" buttonType="primary">
|
||||
{{ (enabled ? "disable" : "enable") | i18n }}
|
||||
</button>
|
||||
<button bitButton bitFormButton type="button" buttonType="secondary" [bitAction]="close">
|
||||
{{ "close" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
</form>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component, EventEmitter, Inject, OnDestroy, OnInit, Output } from "@angular/core";
|
||||
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
|
@ -38,15 +40,21 @@ export class TwoFactorAuthenticatorComponent
|
|||
extends TwoFactorBaseComponent
|
||||
implements OnInit, OnDestroy
|
||||
{
|
||||
@Output() onChangeStatus = new EventEmitter<boolean>();
|
||||
type = TwoFactorProviderType.Authenticator;
|
||||
key: string;
|
||||
token: string;
|
||||
formPromise: Promise<TwoFactorAuthenticatorResponse>;
|
||||
|
||||
override componentName = "app-two-factor-authenticator";
|
||||
private qrScript: HTMLScriptElement;
|
||||
|
||||
protected formGroup = new FormGroup({
|
||||
token: new FormControl(null, [Validators.required]),
|
||||
});
|
||||
|
||||
constructor(
|
||||
@Inject(DIALOG_DATA) protected data: AuthResponse<TwoFactorAuthenticatorResponse>,
|
||||
private dialogRef: DialogRef,
|
||||
apiService: ApiService,
|
||||
i18nService: I18nService,
|
||||
userVerificationService: UserVerificationService,
|
||||
|
@ -68,8 +76,9 @@ export class TwoFactorAuthenticatorComponent
|
|||
this.qrScript.async = true;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
async ngOnInit() {
|
||||
window.document.body.appendChild(this.qrScript);
|
||||
await this.auth(this.data);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
@ -81,17 +90,24 @@ export class TwoFactorAuthenticatorComponent
|
|||
return this.processResponse(authResponse.response);
|
||||
}
|
||||
|
||||
submit() {
|
||||
submit = async () => {
|
||||
if (this.enabled) {
|
||||
return super.disable(this.formPromise);
|
||||
await this.disableAuthentication(this.formPromise);
|
||||
this.onChangeStatus.emit(this.enabled);
|
||||
this.close();
|
||||
} else {
|
||||
return this.enable();
|
||||
await this.enable();
|
||||
this.onChangeStatus.emit(this.enabled);
|
||||
}
|
||||
};
|
||||
|
||||
private async disableAuthentication(promise: Promise<unknown>) {
|
||||
return super.disable(promise);
|
||||
}
|
||||
|
||||
protected async enable() {
|
||||
const request = await this.buildRequestModel(UpdateTwoFactorAuthenticatorRequest);
|
||||
request.token = this.token;
|
||||
request.token = this.formGroup.value.token;
|
||||
request.key = this.key;
|
||||
|
||||
return super.enable(async () => {
|
||||
|
@ -102,7 +118,7 @@ export class TwoFactorAuthenticatorComponent
|
|||
}
|
||||
|
||||
private async processResponse(response: TwoFactorAuthenticatorResponse) {
|
||||
this.token = null;
|
||||
this.formGroup.get("token").setValue(null);
|
||||
this.enabled = response.enabled;
|
||||
this.key = response.key;
|
||||
const email = await firstValueFrom(
|
||||
|
@ -121,4 +137,15 @@ export class TwoFactorAuthenticatorComponent
|
|||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
close = () => {
|
||||
this.dialogRef.close(this.enabled);
|
||||
};
|
||||
|
||||
static open(
|
||||
dialogService: DialogService,
|
||||
config: DialogConfig<AuthResponse<TwoFactorAuthenticatorResponse>>,
|
||||
) {
|
||||
return dialogService.open<boolean>(TwoFactorAuthenticatorComponent, config);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,6 @@
|
|||
</ul>
|
||||
</bit-container>
|
||||
|
||||
<ng-template #authenticatorTemplate></ng-template>
|
||||
<ng-template #duoTemplate></ng-template>
|
||||
<ng-template #emailTemplate></ng-template>
|
||||
<ng-template #yubikeyTemplate></ng-template>
|
||||
|
|
|
@ -34,8 +34,6 @@ import { TwoFactorYubiKeyComponent } from "./two-factor-yubikey.component";
|
|||
templateUrl: "two-factor-setup.component.html",
|
||||
})
|
||||
export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("authenticatorTemplate", { read: ViewContainerRef, static: true })
|
||||
authenticatorModalRef: ViewContainerRef;
|
||||
@ViewChild("yubikeyTemplate", { read: ViewContainerRef, static: true })
|
||||
yubikeyModalRef: ViewContainerRef;
|
||||
@ViewChild("duoTemplate", { read: ViewContainerRef, static: true }) duoModalRef: ViewContainerRef;
|
||||
|
@ -137,12 +135,11 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
|||
if (!result) {
|
||||
return;
|
||||
}
|
||||
const authComp = await this.openModal(
|
||||
this.authenticatorModalRef,
|
||||
TwoFactorAuthenticatorComponent,
|
||||
const authComp: DialogRef<boolean, any> = TwoFactorAuthenticatorComponent.open(
|
||||
this.dialogService,
|
||||
{ data: result },
|
||||
);
|
||||
await authComp.auth(result);
|
||||
authComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => {
|
||||
authComp.componentInstance.onChangeStatus.subscribe((enabled: boolean) => {
|
||||
this.updateStatus(enabled, TwoFactorProviderType.Authenticator);
|
||||
});
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue