From 7fffbc7938ad0ff7cdb46e7487390a6d021d1cfe Mon Sep 17 00:00:00 2001 From: KiruthigaManivannan <162679756+KiruthigaManivannan@users.noreply.github.com> Date: Tue, 25 Jun 2024 20:33:48 +0530 Subject: [PATCH] PM- 2059 Update Two factor webauthn dialog (#9009) * PM-2059 Update Two Factor Webauth Dialog * PM-2059 Added event emitter for enabled status * PM-2059 Addressed review comments * convert to arrow function * PM-2059 Latest comments addressed * PM-2059 Updated disable method by adding a condition to capture simple dialog in base component --------- Co-authored-by: rr-bw <102181210+rr-bw@users.noreply.github.com> --- .../settings/two-factor-setup.component.ts | 11 +- .../two-factor-webauthn.component.html | 256 ++++++++---------- .../settings/two-factor-webauthn.component.ts | 57 +++- 3 files changed, 157 insertions(+), 167 deletions(-) diff --git a/apps/web/src/app/auth/settings/two-factor-setup.component.ts b/apps/web/src/app/auth/settings/two-factor-setup.component.ts index b1592dc72a..10f113d496 100644 --- a/apps/web/src/app/auth/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-setup.component.ts @@ -39,8 +39,6 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { @ViewChild("duoTemplate", { read: ViewContainerRef, static: true }) duoModalRef: ViewContainerRef; @ViewChild("emailTemplate", { read: ViewContainerRef, static: true }) emailModalRef: ViewContainerRef; - @ViewChild("webAuthnTemplate", { read: ViewContainerRef, static: true }) - webAuthnModalRef: ViewContainerRef; organizationId: string; organization: Organization; @@ -192,12 +190,11 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { if (!result) { return; } - const webAuthnComp = await this.openModal( - this.webAuthnModalRef, - TwoFactorWebAuthnComponent, + const webAuthnComp: DialogRef = TwoFactorWebAuthnComponent.open( + this.dialogService, + { data: result }, ); - webAuthnComp.auth(result); - webAuthnComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => { + webAuthnComp.componentInstance.onChangeStatus.subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.WebAuthn); }); break; diff --git a/apps/web/src/app/auth/settings/two-factor-webauthn.component.html b/apps/web/src/app/auth/settings/two-factor-webauthn.component.html index f5a84397c8..9dc9bd4068 100644 --- a/apps/web/src/app/auth/settings/two-factor-webauthn.component.html +++ b/apps/web/src/app/auth/settings/two-factor-webauthn.component.html @@ -1,152 +1,118 @@ - + - + {{ "remove" | i18n }} + + + +
+

{{ "twoFactorWebAuthnAdd" | i18n }}:

+
    +
  1. {{ "twoFactorU2fGiveName" | i18n }}
  2. +
  3. {{ "twoFactorU2fPlugInReadKey" | i18n }}
  4. +
  5. {{ "twoFactorU2fTouchButton" | i18n }}
  6. +
  7. {{ "twoFactorU2fSaveForm" | i18n }}
  8. +
+
+ + {{ "name" | i18n }} + + +
+ + + + + + + + {{ "twoFactorU2fWaiting" | i18n }}... + + + + {{ "twoFactorU2fClickSave" | i18n }} + + + + {{ "twoFactorU2fProblemReadingTryAgain" | i18n }} + + + + + + + + + + diff --git a/apps/web/src/app/auth/settings/two-factor-webauthn.component.ts b/apps/web/src/app/auth/settings/two-factor-webauthn.component.ts index fc2de26721..5e8ea37e93 100644 --- a/apps/web/src/app/auth/settings/two-factor-webauthn.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-webauthn.component.ts @@ -1,4 +1,6 @@ -import { Component, NgZone } from "@angular/core"; +import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; +import { Component, EventEmitter, Inject, NgZone, Output } from "@angular/core"; +import { FormControl, FormGroup } from "@angular/forms"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @@ -31,6 +33,7 @@ interface Key { templateUrl: "two-factor-webauthn.component.html", }) export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent { + @Output() onChangeStatus = new EventEmitter(); type = TwoFactorProviderType.WebAuthn; name: string; keys: Key[]; @@ -44,7 +47,13 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent { override componentName = "app-two-factor-webauthn"; + protected formGroup = new FormGroup({ + name: new FormControl({ value: "", disabled: !this.keyIdAvailable }), + }); + constructor( + @Inject(DIALOG_DATA) protected data: AuthResponse, + private dialogRef: DialogRef, apiService: ApiService, i18nService: I18nService, platformUtilsService: PlatformUtilsService, @@ -61,6 +70,7 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent { userVerificationService, dialogService, ); + this.auth(data); } auth(authResponse: AuthResponse) { @@ -68,7 +78,7 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent { this.processResponse(authResponse.response); } - async submit() { + submit = async () => { if (this.webAuthnResponse == null || this.keyIdAvailable == null) { // Should never happen. return Promise.reject(); @@ -76,16 +86,28 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent { const request = await this.buildRequestModel(UpdateTwoFactorWebAuthnRequest); request.deviceResponse = this.webAuthnResponse; request.id = this.keyIdAvailable; - request.name = this.name; + request.name = this.formGroup.value.name; + return this.enableWebAuth(request); + }; + + private enableWebAuth(request: any) { return super.enable(async () => { this.formPromise = this.apiService.putTwoFactorWebAuthn(request); const response = await this.formPromise; - await this.processResponse(response); + this.processResponse(response); }); } - disable() { + disable = async () => { + await this.disableWebAuth(); + if (!this.enabled) { + this.onChangeStatus.emit(this.enabled); + this.dialogRef.close(); + } + }; + + private async disableWebAuth() { return super.disable(this.formPromise); } @@ -116,19 +138,15 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent { } } - async readKey() { + readKey = async () => { if (this.keyIdAvailable == null) { return; } const request = await this.buildRequestModel(SecretVerificationRequest); - try { - this.challengePromise = this.apiService.getTwoFactorWebAuthnChallenge(request); - const challenge = await this.challengePromise; - this.readDevice(challenge); - } catch (e) { - this.logService.error(e); - } - } + this.challengePromise = this.apiService.getTwoFactorWebAuthnChallenge(request); + const challenge = await this.challengePromise; + this.readDevice(challenge); + }; private readDevice(webAuthnChallenge: ChallengeResponse) { // eslint-disable-next-line @@ -164,7 +182,8 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent { this.resetWebAuthn(); this.keys = []; this.keyIdAvailable = null; - this.name = null; + this.formGroup.get("name").enable(); + this.formGroup.get("name").setValue(null); this.keysConfiguredCount = 0; for (let i = 1; i <= 5; i++) { if (response.keys != null) { @@ -187,5 +206,13 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent { } } this.enabled = response.enabled; + this.onChangeStatus.emit(this.enabled); + } + + static open( + dialogService: DialogService, + config: DialogConfig>, + ) { + return dialogService.open(TwoFactorWebAuthnComponent, config); } }