From 6fd5801739c59809189bd001f8317b142531ad87 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Thu, 30 Mar 2023 15:05:58 +0200 Subject: [PATCH] [EC-598] feat: hash client data and throw if aborted --- .../services/fido2-client.service.spec.ts | 15 ++++++++++++ .../webauthn/services/fido2-client.service.ts | 23 +++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/libs/common/src/webauthn/services/fido2-client.service.spec.ts b/libs/common/src/webauthn/services/fido2-client.service.spec.ts index 9ddf89430d5..882042358c5 100644 --- a/libs/common/src/webauthn/services/fido2-client.service.spec.ts +++ b/libs/common/src/webauthn/services/fido2-client.service.spec.ts @@ -100,6 +100,21 @@ describe("FidoAuthenticatorService", () => { }); }); + describe("aborting", () => { + // Spec: If the options.signal is present and its aborted flag is set to true, return a DOMException whose name is "AbortError" and terminate this algorithm. + it("should throw error if aborting using abort controller", async () => { + const params = createParams({}); + const abortController = new AbortController(); + abortController.abort(); + + const result = async () => await client.createCredential(params, abortController); + + const rejects = expect(result).rejects; + await rejects.toMatchObject({ name: "AbortError" }); + await rejects.toBeInstanceOf(DOMException); + }); + }); + function createParams(params: Partial = {}): CreateCredentialParams { return { origin: params.origin ?? "bitwarden.com", diff --git a/libs/common/src/webauthn/services/fido2-client.service.ts b/libs/common/src/webauthn/services/fido2-client.service.ts index ec06e2a00b7..e625ceecc7f 100644 --- a/libs/common/src/webauthn/services/fido2-client.service.ts +++ b/libs/common/src/webauthn/services/fido2-client.service.ts @@ -1,5 +1,6 @@ import { parse } from "tldts"; +import { Utils } from "../../misc/utils"; import { Fido2AuthenticatorService } from "../abstractions/fido2-authenticator.service.abstraction"; import { AssertCredentialParams, @@ -14,9 +15,9 @@ import { Fido2Utils } from "../abstractions/fido2-utils"; export class Fido2ClientService implements Fido2ClientServiceAbstraction { constructor(private authenticator: Fido2AuthenticatorService) {} - createCredential( + async createCredential( params: CreateCredentialParams, - abortController?: AbortController + abortController: AbortController = new AbortController() ): Promise { if (!params.sameOriginWithAncestors) { throw new DOMException("Invalid 'sameOriginWithAncestors' value", "NotAllowedError"); @@ -52,6 +53,24 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { if (credTypesAndPubKeyAlgs.length === 0) { throw new DOMException("No supported key algorithms were found", "NotSupportedError"); } + + const collectedClientData = { + type: "webauthn.create", + challenge: params.challenge, + origin: params.origin, + crossOrigin: !params.sameOriginWithAncestors, + // tokenBinding: {} // Not currently supported + }; + const clientDataJSON = JSON.stringify(collectedClientData); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const clientDataHash = await crypto.subtle.digest( + { name: "SHA-256" }, + Utils.fromByteStringToArray(clientDataJSON) + ); + + if (abortController.signal.aborted) { + throw new DOMException(undefined, "AbortError"); + } } assertCredential(