From 46ce822b30ed279a813a821d558ff866c58d154d Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 8 May 2023 14:02:55 +0200 Subject: [PATCH] [PM-2014] chore: refactor api logic into new api service and move ui logic into existing service --- .../services/webauthn/webauthn-api.service.ts | 22 +++++++++++ .../webauthn/webauthn.service.spec.ts | 0 .../services/webauthn/webauthn.service.ts | 38 +++++++++++++++---- .../create-credential-dialog.component.ts | 38 +++---------------- 4 files changed, 59 insertions(+), 39 deletions(-) create mode 100644 apps/web/src/app/auth/core/services/webauthn/webauthn-api.service.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn/webauthn.service.spec.ts diff --git a/apps/web/src/app/auth/core/services/webauthn/webauthn-api.service.ts b/apps/web/src/app/auth/core/services/webauthn/webauthn-api.service.ts new file mode 100644 index 00000000000..f422a9b8b7f --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn/webauthn-api.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from "@angular/core"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction"; +import { ChallengeResponse } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response"; +import { Verification } from "@bitwarden/common/types/verification"; + +import { CoreAuthModule } from "../../core.module"; + +@Injectable({ providedIn: CoreAuthModule }) +export class WebauthnApiService { + constructor( + private apiService: ApiService, + private userVerificationService: UserVerificationService + ) {} + + async getChallenge(verification: Verification): Promise { + const request = await this.userVerificationService.buildRequest(verification); + const response = await this.apiService.send("POST", "/webauthn/options", request, true, true); + return new ChallengeResponse(response); + } +} diff --git a/apps/web/src/app/auth/core/services/webauthn/webauthn.service.spec.ts b/apps/web/src/app/auth/core/services/webauthn/webauthn.service.spec.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/web/src/app/auth/core/services/webauthn/webauthn.service.ts b/apps/web/src/app/auth/core/services/webauthn/webauthn.service.ts index 9e2e67eeb7a..7ef48ef4ecd 100644 --- a/apps/web/src/app/auth/core/services/webauthn/webauthn.service.ts +++ b/apps/web/src/app/auth/core/services/webauthn/webauthn.service.ts @@ -1,22 +1,46 @@ import { Injectable } from "@angular/core"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { ChallengeResponse } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response"; +import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { Verification } from "@bitwarden/common/types/verification"; import { CoreAuthModule } from "../../core.module"; +import { WebauthnApiService } from "./webauthn-api.service"; + +type WebauthnCredentialView = unknown; + +export class UserVerificationFailedError extends Error {} + @Injectable({ providedIn: CoreAuthModule }) export class WebauthnService { constructor( - private apiService: ApiService, - private userVerificationService: UserVerificationService + private apiService: WebauthnApiService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService ) {} async newCredentialOptions(verification: Verification): Promise { - const request = await this.userVerificationService.buildRequest(verification); - const response = await this.apiService.send("POST", "/webauthn/options", request, true, true); - return new ChallengeResponse(response); + try { + return await this.apiService.getChallenge(verification); + } catch (error) { + if (error instanceof ErrorResponse && error.statusCode === 400) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("error"), + this.i18nService.t("invalidMasterPassword") + ); + } else { + this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); + } + return undefined; + } + } + + async createCredential(challenge: ChallengeResponse): Promise { + await new Promise((_, reject) => setTimeout(() => reject(new Error("Not implemented")), 1000)); + return undefined; } } diff --git a/apps/web/src/app/auth/settings/fido2-login-settings/create-credential-dialog/create-credential-dialog.component.ts b/apps/web/src/app/auth/settings/fido2-login-settings/create-credential-dialog/create-credential-dialog.component.ts index 135851c3b22..494adc1630d 100644 --- a/apps/web/src/app/auth/settings/fido2-login-settings/create-credential-dialog/create-credential-dialog.component.ts +++ b/apps/web/src/app/auth/settings/fido2-login-settings/create-credential-dialog/create-credential-dialog.component.ts @@ -3,11 +3,8 @@ import { Component } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; -import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { ChallengeResponse } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response"; -import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { WebauthnService } from "../../../core"; @@ -46,9 +43,7 @@ export class CreateCredentialDialogComponent { constructor( private formBuilder: FormBuilder, private dialogRef: DialogRef, - private webauthnService: WebauthnService, - private platformUtilsService: PlatformUtilsService, - private i18nService: I18nService + private webauthnService: WebauthnService ) {} protected submit = async () => { @@ -61,7 +56,10 @@ export class CreateCredentialDialogComponent { return; } - this.challenge = await this.getNewCredentialOptions(); + this.challenge = await this.webauthnService.newCredentialOptions({ + type: VerificationType.MasterPassword, + secret: this.formGroup.value.userVerification.masterPassword, + }); if (this.challenge === undefined) { return; } @@ -74,7 +72,7 @@ export class CreateCredentialDialogComponent { if (this.currentStep === "credentialCreation") { try { - await this.createCredential(); + await this.webauthnService.createCredential(this.challenge); } catch { this.currentStep = "credentialCreationFailed"; } @@ -83,30 +81,6 @@ export class CreateCredentialDialogComponent { this.dialogRef.disableClose = false; } }; - - private async getNewCredentialOptions(): Promise { - try { - return await this.webauthnService.newCredentialOptions({ - type: VerificationType.MasterPassword, - secret: this.formGroup.value.userVerification.masterPassword, - }); - } catch (error) { - if (error instanceof ErrorResponse && error.statusCode === 400) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("error"), - this.i18nService.t("invalidMasterPassword") - ); - } else { - this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); - } - return undefined; - } - } - - private async createCredential() { - await new Promise((_, reject) => setTimeout(() => reject(new Error("Not implemented")), 1000)); - } } /**