From 317d652088c5b05a5b3ee2410e3510af460c9cd4 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 1 Nov 2023 09:26:41 +0100 Subject: [PATCH] [PM-3722] Use `UserVerificationPrompt` in passkey registration dialog (#6422) * [PM-3722] fix: wrong translation bug * [PM-3722] feat: use user verification component during creation * [PM-3722] feat: use user verification component during deletion * [PM-3722] feat: improve error handling --- .../create-credential-dialog.component.html | 12 ++++---- .../create-credential-dialog.component.ts | 30 +++++++++++-------- .../delete-credential-dialog.component.html | 10 +++---- .../delete-credential-dialog.component.ts | 22 +++++++------- .../webauthn-login-settings.module.ts | 3 +- .../components/user-verification.component.ts | 4 +-- 6 files changed, 41 insertions(+), 40 deletions(-) diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html index aadcf5e596..bc78d78551 100644 --- a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html @@ -9,12 +9,12 @@

{{ "passkeyEnterMasterPassword" | i18n }}

- - {{ "masterPassword" | i18n }} - - - {{ "confirmIdentity" | i18n }} - + + +
diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts index 12af83cac5..cf9265088a 100644 --- a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts @@ -3,11 +3,11 @@ import { Component, OnInit } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { firstValueFrom, map, Observable } from "rxjs"; -import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; 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 { Verification } from "@bitwarden/common/types/verification"; import { DialogService } from "@bitwarden/components"; import { WebauthnLoginService } from "../../../core"; @@ -35,9 +35,10 @@ export class CreateCredentialDialogComponent implements OnInit { protected readonly Icons = { CreatePasskeyIcon, CreatePasskeyFailedIcon }; protected currentStep: Step = "userVerification"; + protected invalidSecret = false; protected formGroup = this.formBuilder.group({ userVerification: this.formBuilder.group({ - masterPassword: ["", [Validators.required]], + secret: [null as Verification | null, Validators.required], }), credentialNaming: this.formBuilder.group({ name: ["", Validators.maxLength(50)], @@ -89,20 +90,19 @@ export class CreateCredentialDialogComponent implements OnInit { } try { - this.credentialOptions = await this.webauthnService.getCredentialCreateOptions({ - type: VerificationType.MasterPassword, - secret: this.formGroup.value.userVerification.masterPassword, - }); + this.credentialOptions = await this.webauthnService.getCredentialCreateOptions( + this.formGroup.value.userVerification.secret + ); } catch (error) { if (error instanceof ErrorResponse && error.statusCode === 400) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("error"), - this.i18nService.t("invalidMasterPassword") - ); + this.invalidSecret = true; } else { this.logService?.error(error); - this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); + this.platformUtilsService.showToast( + "error", + this.i18nService.t("unexpectedError"), + error.message + ); } return; } @@ -141,7 +141,11 @@ export class CreateCredentialDialogComponent implements OnInit { ); } catch (error) { this.logService?.error(error); - this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); + this.platformUtilsService.showToast( + "error", + this.i18nService.t("unexpectedError"), + error.message + ); return; } diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.html b/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.html index 5e87f6d4ad..147fc9874d 100644 --- a/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.html +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.html @@ -14,12 +14,10 @@

{{ "removePasskeyInfo" | i18n }}

- - {{ "masterPassword" | i18n }} - - - {{ "confirmIdentity" | i18n }} - +
diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.ts index 9ee1337ffb..f1c8045fd1 100644 --- a/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.ts +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.ts @@ -1,13 +1,13 @@ import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject, OnDestroy, OnInit } from "@angular/core"; -import { FormBuilder, Validators } from "@angular/forms"; +import { FormBuilder } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; -import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; 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 { Verification } from "@bitwarden/common/types/verification"; import { DialogService } from "@bitwarden/components"; import { WebauthnLoginService } from "../../../core"; @@ -23,8 +23,9 @@ export interface DeleteCredentialDialogParams { export class DeleteCredentialDialogComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); + protected invalidSecret = false; protected formGroup = this.formBuilder.group({ - masterPassword: ["", [Validators.required]], + secret: null as Verification | null, }); protected credential?: WebauthnCredentialView; protected loading$ = this.webauthnService.loading$; @@ -53,21 +54,18 @@ export class DeleteCredentialDialogComponent implements OnInit, OnDestroy { this.dialogRef.disableClose = true; try { - await this.webauthnService.deleteCredential(this.credential.id, { - type: VerificationType.MasterPassword, - secret: this.formGroup.value.masterPassword, - }); + await this.webauthnService.deleteCredential(this.credential.id, this.formGroup.value.secret); this.platformUtilsService.showToast("success", null, this.i18nService.t("passkeyRemoved")); } catch (error) { if (error instanceof ErrorResponse && error.statusCode === 400) { + this.invalidSecret = true; + } else { + this.logService?.error(error); this.platformUtilsService.showToast( "error", - this.i18nService.t("error"), - this.i18nService.t("invalidMasterPassword") + this.i18nService.t("unexpectedError"), + error.message ); - } else { - this.logService.error(error); - this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); } return false; } finally { diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.module.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.module.ts index 73e21387ec..5a7bd8ebca 100644 --- a/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.module.ts +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.module.ts @@ -2,13 +2,14 @@ import { NgModule } from "@angular/core"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { SharedModule } from "../../../shared/shared.module"; +import { UserVerificationModule } from "../../shared/components/user-verification"; import { CreateCredentialDialogComponent } from "./create-credential-dialog/create-credential-dialog.component"; import { DeleteCredentialDialogComponent } from "./delete-credential-dialog/delete-credential-dialog.component"; import { WebauthnLoginSettingsComponent } from "./webauthn-login-settings.component"; @NgModule({ - imports: [SharedModule, FormsModule, ReactiveFormsModule], + imports: [SharedModule, FormsModule, ReactiveFormsModule, UserVerificationModule], declarations: [ WebauthnLoginSettingsComponent, CreateCredentialDialogComponent, diff --git a/libs/angular/src/auth/components/user-verification.component.ts b/libs/angular/src/auth/components/user-verification.component.ts index b2d4d8a4c3..9f512fb350 100644 --- a/libs/angular/src/auth/components/user-verification.component.ts +++ b/libs/angular/src/auth/components/user-verification.component.ts @@ -51,8 +51,8 @@ export class UserVerificationComponent implements ControlValueAccessor, OnInit, return { invalidSecret: { message: this.hasMasterPassword - ? this.i18nService.t("incorrectCode") - : this.i18nService.t("incorrectPassword"), + ? this.i18nService.t("incorrectPassword") + : this.i18nService.t("incorrectCode"), }, }; }