From f60a37fe2c378e234186420233b0971b56880542 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Mon, 1 Apr 2024 11:56:35 -0500 Subject: [PATCH] [PM-6426] Implementing abortTimeout for Fido2ClientService using TaskSchedulerService --- .../browser/src/background/main.background.ts | 1 + .../fido2/fido2-client.service.spec.ts | 4 + .../services/fido2/fido2-client.service.ts | 87 +++++++++++-------- 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 340b7c39f2..5c3a062993 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -830,6 +830,7 @@ export default class MainBackground { this.authService, this.vaultSettingsService, this.domainSettingsService, + this.taskSchedulerService, this.logService, ); diff --git a/libs/common/src/vault/services/fido2/fido2-client.service.spec.ts b/libs/common/src/vault/services/fido2/fido2-client.service.spec.ts index 9757e24d8f..797d45b441 100644 --- a/libs/common/src/vault/services/fido2/fido2-client.service.spec.ts +++ b/libs/common/src/vault/services/fido2/fido2-client.service.spec.ts @@ -5,6 +5,7 @@ import { AuthService } from "../../../auth/abstractions/auth.service"; import { AuthenticationStatus } from "../../../auth/enums/authentication-status"; import { DomainSettingsService } from "../../../autofill/services/domain-settings.service"; import { ConfigService } from "../../../platform/abstractions/config/config.service"; +import { TaskSchedulerService } from "../../../platform/abstractions/task-scheduler.service"; import { Utils } from "../../../platform/misc/utils"; import { Fido2AuthenticatorError, @@ -34,6 +35,7 @@ describe("FidoAuthenticatorService", () => { let authService!: MockProxy; let vaultSettingsService: MockProxy; let domainSettingsService: MockProxy; + let taskSchedulerService: MockProxy; let client!: Fido2ClientService; let tab!: chrome.tabs.Tab; @@ -43,6 +45,7 @@ describe("FidoAuthenticatorService", () => { authService = mock(); vaultSettingsService = mock(); domainSettingsService = mock(); + taskSchedulerService = mock(); client = new Fido2ClientService( authenticator, @@ -50,6 +53,7 @@ describe("FidoAuthenticatorService", () => { authService, vaultSettingsService, domainSettingsService, + taskSchedulerService, ); configService.serverConfig$ = of({ environment: { vault: VaultUrl } } as any); vaultSettingsService.enablePasskeys$ = of(true); diff --git a/libs/common/src/vault/services/fido2/fido2-client.service.ts b/libs/common/src/vault/services/fido2/fido2-client.service.ts index bfc8cbe915..c7b47d11c7 100644 --- a/libs/common/src/vault/services/fido2/fido2-client.service.ts +++ b/libs/common/src/vault/services/fido2/fido2-client.service.ts @@ -6,6 +6,8 @@ import { AuthenticationStatus } from "../../../auth/enums/authentication-status" import { DomainSettingsService } from "../../../autofill/services/domain-settings.service"; import { ConfigService } from "../../../platform/abstractions/config/config.service"; import { LogService } from "../../../platform/abstractions/log.service"; +import { TaskSchedulerService } from "../../../platform/abstractions/task-scheduler.service"; +import { ScheduledTaskNames } from "../../../platform/enums/scheduled-task-name.enum"; import { Utils } from "../../../platform/misc/utils"; import { Fido2AuthenticatorError, @@ -38,12 +40,26 @@ import { Fido2Utils } from "./fido2-utils"; * It is highly recommended that the W3C specification is used a reference when reading this code. */ export class Fido2ClientService implements Fido2ClientServiceAbstraction { + private readonly TIMEOUTS = { + NO_VERIFICATION: { + DEFAULT: 120000, + MIN: 30000, + MAX: 180000, + }, + WITH_VERIFICATION: { + DEFAULT: 300000, + MIN: 30000, + MAX: 600000, + }, + }; + constructor( private authenticator: Fido2AuthenticatorService, private configService: ConfigService, private authService: AuthService, private vaultSettingsService: VaultSettingsService, private domainSettingsService: DomainSettingsService, + private taskSchedulerService: TaskSchedulerService, private logService?: LogService, ) {} @@ -151,7 +167,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { this.logService?.info(`[Fido2Client] Aborted with AbortController`); throw new DOMException("The operation either timed out or was not allowed.", "AbortError"); } - const timeout = setAbortTimeout( + const timeout = await this.setAbortTimeout( abortController, params.authenticatorSelection?.userVerification, params.timeout, @@ -200,7 +216,11 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { }; } - clearTimeout(timeout); + await this.taskSchedulerService.clearScheduledTask({ + taskName: ScheduledTaskNames.fido2ClientAbortTimeout, + timeoutId: timeout, + }); + return { credentialId: Fido2Utils.bufferToString(makeCredentialResult.credentialId), attestationObject: Fido2Utils.bufferToString(makeCredentialResult.attestationObject), @@ -260,7 +280,11 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { throw new DOMException("The operation either timed out or was not allowed.", "AbortError"); } - const timeout = setAbortTimeout(abortController, params.userVerification, params.timeout); + const timeout = await this.setAbortTimeout( + abortController, + params.userVerification, + params.timeout, + ); let getAssertionResult; try { @@ -297,7 +321,10 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { this.logService?.info(`[Fido2Client] Aborted with AbortController`); throw new DOMException("The operation either timed out or was not allowed.", "AbortError"); } - clearTimeout(timeout); + await this.taskSchedulerService.clearScheduledTask({ + taskName: ScheduledTaskNames.fido2ClientAbortTimeout, + timeoutId: timeout, + }); return { authenticatorData: Fido2Utils.bufferToString(getAssertionResult.authenticatorData), @@ -310,43 +337,29 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { signature: Fido2Utils.bufferToString(getAssertionResult.signature), }; } -} -const TIMEOUTS = { - NO_VERIFICATION: { - DEFAULT: 120000, - MIN: 30000, - MAX: 180000, - }, - WITH_VERIFICATION: { - DEFAULT: 300000, - MIN: 30000, - MAX: 600000, - }, -}; + private setAbortTimeout = async ( + abortController: AbortController, + userVerification?: UserVerification, + timeout?: number, + ): Promise => { + let clampedTimeout: number; -function setAbortTimeout( - abortController: AbortController, - userVerification?: UserVerification, - timeout?: number, -): number { - let clampedTimeout: number; + const { WITH_VERIFICATION, NO_VERIFICATION } = this.TIMEOUTS; + if (userVerification === "required") { + timeout = timeout ?? WITH_VERIFICATION.DEFAULT; + clampedTimeout = Math.max(WITH_VERIFICATION.MIN, Math.min(timeout, WITH_VERIFICATION.MAX)); + } else { + timeout = timeout ?? NO_VERIFICATION.DEFAULT; + clampedTimeout = Math.max(NO_VERIFICATION.MIN, Math.min(timeout, NO_VERIFICATION.MAX)); + } - if (userVerification === "required") { - timeout = timeout ?? TIMEOUTS.WITH_VERIFICATION.DEFAULT; - clampedTimeout = Math.max( - TIMEOUTS.WITH_VERIFICATION.MIN, - Math.min(timeout, TIMEOUTS.WITH_VERIFICATION.MAX), + return await this.taskSchedulerService.setTimeout( + () => abortController.abort(), + clampedTimeout, + ScheduledTaskNames.fido2ClientAbortTimeout, ); - } else { - timeout = timeout ?? TIMEOUTS.NO_VERIFICATION.DEFAULT; - clampedTimeout = Math.max( - TIMEOUTS.NO_VERIFICATION.MIN, - Math.min(timeout, TIMEOUTS.NO_VERIFICATION.MAX), - ); - } - - return window.setTimeout(() => abortController.abort(), clampedTimeout); + }; } /**