1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-06 11:43:51 +00:00

[PM-2014] feat: fetch webauthn challenge

This commit is contained in:
Andreas Coroiu
2023-05-08 13:41:58 +02:00
parent 9e6215013d
commit 6baaafe856
2 changed files with 54 additions and 5 deletions

View File

@@ -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 WebauthnService {
constructor(
private apiService: ApiService,
private userVerificationService: UserVerificationService
) {}
async newCredentialOptions(verification: Verification): Promise<ChallengeResponse> {
const request = await this.userVerificationService.buildRequest(verification);
const response = await this.apiService.send("POST", "/webauthn/options", request, true, true);
return new ChallengeResponse(response);
}
}

View File

@@ -3,6 +3,11 @@ 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";
@@ -36,11 +41,14 @@ export class CreateCredentialDialogComponent {
name: ["", Validators.maxLength(50)],
}),
});
protected challenge?: ChallengeResponse;
constructor(
private formBuilder: FormBuilder,
private dialogRef: DialogRef,
private webauthnService: WebauthnService
private webauthnService: WebauthnService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService
) {}
protected submit = async () => {
@@ -52,7 +60,11 @@ export class CreateCredentialDialogComponent {
if (this.formGroup.controls.userVerification.invalid) {
return;
}
await this.verifyUser();
this.challenge = await this.getNewCredentialOptions();
if (this.challenge === undefined) {
return;
}
this.currentStep = "credentialCreation";
}
@@ -72,9 +84,24 @@ export class CreateCredentialDialogComponent {
}
};
private async verifyUser() {
// Mocked
await new Promise((resolve) => setTimeout(resolve, 1000));
private async getNewCredentialOptions(): Promise<ChallengeResponse | undefined> {
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() {