1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 00:33:44 +00:00

[EC-598] feat: add logging to fido2 client

This commit is contained in:
Andreas Coroiu
2023-04-27 15:51:51 +02:00
parent 89bfdbf03e
commit 2d390f0492
2 changed files with 36 additions and 2 deletions

View File

@@ -507,7 +507,10 @@ export default class MainBackground {
this.fido2UserInterfaceService, this.fido2UserInterfaceService,
this.logService this.logService
); );
this.fido2ClientService = new Fido2ClientService(this.fido2AuthenticatorService); this.fido2ClientService = new Fido2ClientService(
this.fido2AuthenticatorService,
this.logService
);
const systemUtilsServiceReloadCallback = () => { const systemUtilsServiceReloadCallback = () => {
const forceWindowReload = const forceWindowReload =

View File

@@ -1,5 +1,6 @@
import { parse } from "tldts"; import { parse } from "tldts";
import { LogService } from "../../abstractions/log.service";
import { Utils } from "../../misc/utils"; import { Utils } from "../../misc/utils";
import { import {
Fido2AutenticatorError, Fido2AutenticatorError,
@@ -25,18 +26,24 @@ import { Fido2Utils } from "../abstractions/fido2-utils";
import { isValidRpId } from "./domain-utils"; import { isValidRpId } from "./domain-utils";
export class Fido2ClientService implements Fido2ClientServiceAbstraction { export class Fido2ClientService implements Fido2ClientServiceAbstraction {
constructor(private authenticator: Fido2AuthenticatorService) {} constructor(private authenticator: Fido2AuthenticatorService, private logService?: LogService) {}
async createCredential( async createCredential(
params: CreateCredentialParams, params: CreateCredentialParams,
abortController = new AbortController() abortController = new AbortController()
): Promise<CreateCredentialResult> { ): Promise<CreateCredentialResult> {
if (!params.sameOriginWithAncestors) { if (!params.sameOriginWithAncestors) {
this.logService?.warning(
`[Fido2Client] Invalid 'sameOriginWithAncestors' value: ${params.sameOriginWithAncestors}`
);
throw new DOMException("Invalid 'sameOriginWithAncestors' value", "NotAllowedError"); throw new DOMException("Invalid 'sameOriginWithAncestors' value", "NotAllowedError");
} }
const userId = Fido2Utils.stringToBuffer(params.user.id); const userId = Fido2Utils.stringToBuffer(params.user.id);
if (userId.length < 1 || userId.length > 64) { if (userId.length < 1 || userId.length > 64) {
this.logService?.warning(
`[Fido2Client] Invalid 'user.id' length: ${params.user.id} (${userId.length})`
);
throw new TypeError("Invalid 'user.id' length"); throw new TypeError("Invalid 'user.id' length");
} }
@@ -44,10 +51,14 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
const rpId = params.rp.id ?? parsedOrigin.hostname; const rpId = params.rp.id ?? parsedOrigin.hostname;
if (parsedOrigin.hostname == undefined || !params.origin.startsWith("https://")) { if (parsedOrigin.hostname == undefined || !params.origin.startsWith("https://")) {
this.logService?.warning(`[Fido2Client] Invalid https origin: ${params.origin}`);
throw new DOMException("'origin' is not a valid https origin", "SecurityError"); throw new DOMException("'origin' is not a valid https origin", "SecurityError");
} }
if (!isValidRpId(rpId, params.origin)) { if (!isValidRpId(rpId, params.origin)) {
this.logService?.warning(
`[Fido2Client] 'rp.id' cannot be used with the current origin: rp.id = ${rpId}; origin = ${params.origin}`
);
throw new DOMException("'rp.id' cannot be used with the current origin", "SecurityError"); throw new DOMException("'rp.id' cannot be used with the current origin", "SecurityError");
} }
@@ -64,6 +75,10 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
} }
if (credTypesAndPubKeyAlgs.length === 0) { if (credTypesAndPubKeyAlgs.length === 0) {
const requestedAlgorithms = credTypesAndPubKeyAlgs.map((p) => p.alg).join(", ");
this.logService?.warning(
`[Fido2Client] No compatible algorithms found, RP requested: ${requestedAlgorithms}`
);
throw new DOMException("No supported key algorithms were found", "NotSupportedError"); throw new DOMException("No supported key algorithms were found", "NotSupportedError");
} }
@@ -78,6 +93,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
const clientDataJSONBytes = Utils.fromByteStringToArray(clientDataJSON); const clientDataJSONBytes = Utils.fromByteStringToArray(clientDataJSON);
const clientDataHash = await crypto.subtle.digest({ name: "SHA-256" }, clientDataJSONBytes); const clientDataHash = await crypto.subtle.digest({ name: "SHA-256" }, clientDataJSONBytes);
if (abortController.signal.aborted) { if (abortController.signal.aborted) {
this.logService?.info(`[Fido2Client] Aborted with AbortController`);
throw new DOMException(undefined, "AbortError"); throw new DOMException(undefined, "AbortError");
} }
const timeout = setAbortTimeout( const timeout = setAbortTimeout(
@@ -123,6 +139,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
abortController.signal.aborted && abortController.signal.aborted &&
abortController.signal.reason === UserRequestedFallbackAbortReason abortController.signal.reason === UserRequestedFallbackAbortReason
) { ) {
this.logService?.info(`[Fido2Client] Aborting because user requested fallback`);
throw new FallbackRequestedError(); throw new FallbackRequestedError();
} }
@@ -130,13 +147,16 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
error instanceof Fido2AutenticatorError && error instanceof Fido2AutenticatorError &&
error.errorCode === Fido2AutenticatorErrorCode.InvalidState error.errorCode === Fido2AutenticatorErrorCode.InvalidState
) { ) {
this.logService?.warning(`[Fido2Client] Unknown error: ${error}`);
throw new DOMException(undefined, "InvalidStateError"); throw new DOMException(undefined, "InvalidStateError");
} }
this.logService?.info(`[Fido2Client] Aborted by user: ${error}`);
throw new DOMException(undefined, "NotAllowedError"); throw new DOMException(undefined, "NotAllowedError");
} }
if (abortController.signal.aborted) { if (abortController.signal.aborted) {
this.logService?.info(`[Fido2Client] Aborted with AbortController`);
throw new DOMException(undefined, "AbortError"); throw new DOMException(undefined, "AbortError");
} }
@@ -157,6 +177,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
): Promise<AssertCredentialResult> { ): Promise<AssertCredentialResult> {
const { domain: effectiveDomain } = parse(params.origin, { allowPrivateDomains: true }); const { domain: effectiveDomain } = parse(params.origin, { allowPrivateDomains: true });
if (effectiveDomain == undefined) { if (effectiveDomain == undefined) {
this.logService?.warning(`[Fido2Client] Invalid origin: ${params.origin}`);
throw new DOMException("'origin' is not a valid domain", "SecurityError"); throw new DOMException("'origin' is not a valid domain", "SecurityError");
} }
@@ -164,10 +185,14 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
const rpId = params.rpId ?? parsedOrigin.hostname; const rpId = params.rpId ?? parsedOrigin.hostname;
if (parsedOrigin.hostname == undefined || !params.origin.startsWith("https://")) { if (parsedOrigin.hostname == undefined || !params.origin.startsWith("https://")) {
this.logService?.warning(`[Fido2Client] Invalid https origin: ${params.origin}`);
throw new DOMException("'origin' is not a valid https origin", "SecurityError"); throw new DOMException("'origin' is not a valid https origin", "SecurityError");
} }
if (!isValidRpId(rpId, params.origin)) { if (!isValidRpId(rpId, params.origin)) {
this.logService?.warning(
`[Fido2Client] 'rp.id' cannot be used with the current origin: rp.id = ${rpId}; origin = ${params.origin}`
);
throw new DOMException("'rp.id' cannot be used with the current origin", "SecurityError"); throw new DOMException("'rp.id' cannot be used with the current origin", "SecurityError");
} }
@@ -183,6 +208,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
const clientDataHash = await crypto.subtle.digest({ name: "SHA-256" }, clientDataJSONBytes); const clientDataHash = await crypto.subtle.digest({ name: "SHA-256" }, clientDataJSONBytes);
if (abortController.signal.aborted) { if (abortController.signal.aborted) {
this.logService?.info(`[Fido2Client] Aborted with AbortController`);
throw new DOMException(undefined, "AbortError"); throw new DOMException(undefined, "AbortError");
} }
@@ -213,6 +239,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
abortController.signal.aborted && abortController.signal.aborted &&
abortController.signal.reason === UserRequestedFallbackAbortReason abortController.signal.reason === UserRequestedFallbackAbortReason
) { ) {
this.logService?.info(`[Fido2Client] Aborting because user requested fallback`);
throw new FallbackRequestedError(); throw new FallbackRequestedError();
} }
@@ -220,12 +247,16 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
error instanceof Fido2AutenticatorError && error instanceof Fido2AutenticatorError &&
error.errorCode === Fido2AutenticatorErrorCode.InvalidState error.errorCode === Fido2AutenticatorErrorCode.InvalidState
) { ) {
this.logService?.warning(`[Fido2Client] Unknown error: ${error}`);
throw new DOMException(undefined, "InvalidStateError"); throw new DOMException(undefined, "InvalidStateError");
} }
this.logService?.info(`[Fido2Client] Aborted by user: ${error}`);
throw new DOMException(undefined, "NotAllowedError"); throw new DOMException(undefined, "NotAllowedError");
} }
if (abortController.signal.aborted) { if (abortController.signal.aborted) {
this.logService?.info(`[Fido2Client] Aborted with AbortController`);
throw new DOMException(undefined, "AbortError"); throw new DOMException(undefined, "AbortError");
} }
clearTimeout(timeout); clearTimeout(timeout);