diff --git a/apps/browser/src/content/webauthn/messaging/message.ts b/apps/browser/src/content/webauthn/messaging/message.ts index d77aea0b5b3..36b907c4674 100644 --- a/apps/browser/src/content/webauthn/messaging/message.ts +++ b/apps/browser/src/content/webauthn/messaging/message.ts @@ -14,6 +14,7 @@ export enum MessageType { CredentialGetResponse, AbortRequest, AbortResponse, + ErrorResponse, } export type CredentialCreationRequest = { @@ -43,6 +44,11 @@ export type AbortRequest = { abortedRequestId: string; }; +export type ErrorResponse = { + type: MessageType.ErrorResponse; + error: string; +}; + export type AbortResponse = { type: MessageType.AbortResponse; abortedRequestId: string; @@ -54,4 +60,5 @@ export type Message = | CredentialGetRequest | CredentialGetResponse | AbortRequest - | AbortResponse; + | AbortResponse + | ErrorResponse; diff --git a/apps/browser/src/content/webauthn/messaging/messenger.spec.ts b/apps/browser/src/content/webauthn/messaging/messenger.spec.ts index 855860f5e43..f9a5b92a875 100644 --- a/apps/browser/src/content/webauthn/messaging/messenger.spec.ts +++ b/apps/browser/src/content/webauthn/messaging/messenger.spec.ts @@ -44,6 +44,17 @@ describe("Messenger", () => { expect(returned).toMatchObject(response); }); + it("should throw error from B when sending request from A that fails", async () => { + const request = createRequest(); + const error = new Error("Test error"); + const requestPromise = messengerA.request(request); + const received = handlerB.recieve(); + + received[0].reject(error); + + await expect(requestPromise).rejects.toThrow(); + }); + it("should deliver abort signal to B when requesting abort", () => { const abortController = new AbortController(); messengerA.request(createRequest(), abortController); @@ -53,15 +64,6 @@ describe("Messenger", () => { expect(received[0].abortController.signal.aborted).toBe(true); }); - - it.skip("should abort request and throw error when abort is requested from A", () => { - const abortController = new AbortController(); - const requestPromise = messengerA.request(createRequest(), abortController); - - abortController.abort(); - - expect(requestPromise).toThrow(); - }); }); type TestMessage = Message & { testId: string }; @@ -105,6 +107,7 @@ class TestMessageHandler { private recievedMessages: { message: TestMessage; respond: (response: TestMessage) => void; + reject: (error: Error) => void; abortController?: AbortController; }[] = []; @@ -115,6 +118,7 @@ class TestMessageHandler { message, abortController, respond: (response) => resolve(response), + reject: (error) => reject(error), }); }); } diff --git a/apps/browser/src/content/webauthn/messaging/messenger.ts b/apps/browser/src/content/webauthn/messaging/messenger.ts index 68af7b29951..aa09b676bcd 100644 --- a/apps/browser/src/content/webauthn/messaging/messenger.ts +++ b/apps/browser/src/content/webauthn/messaging/messenger.ts @@ -49,15 +49,26 @@ export class Messenger { const abortController = new AbortController(); this.abortControllers.set(message.metadata.requestId, abortController); - const handlerResponse = await this.handler(message, abortController); - this.abortControllers.delete(message.metadata.requestId); - if (handlerResponse === undefined) { - return; + try { + const handlerResponse = await this.handler(message, abortController); + + if (handlerResponse === undefined) { + return; + } + + const metadata: Metadata = { requestId: message.metadata.requestId }; + this.channel.postMessage({ ...handlerResponse, metadata }); + } catch (error) { + const metadata: Metadata = { requestId: message.metadata.requestId }; + this.channel.postMessage({ + type: MessageType.ErrorResponse, + metadata, + error: JSON.stringify(error, Object.getOwnPropertyNames(error)), + }); + } finally { + this.abortControllers.delete(message.metadata.requestId); } - - const metadata: Metadata = { requestId: message.metadata.requestId }; - this.channel.postMessage({ ...handlerResponse, metadata }); }) ) .subscribe(); @@ -71,7 +82,7 @@ export class Messenger { }); } - request(request: Message, abortController?: AbortController): Promise { + async request(request: Message, abortController?: AbortController): Promise { const requestId = Date.now().toString(); const metadata: Metadata = { requestId }; @@ -93,8 +104,15 @@ export class Messenger { this.channel.postMessage({ ...request, metadata }); - return promise.finally(() => - abortController?.signal.removeEventListener("abort", abortListener) - ); + const response = await promise; + abortController?.signal.removeEventListener("abort", abortListener); + + if (response.type === MessageType.ErrorResponse) { + const error = new Error(); + Object.assign(error, JSON.parse(response.error)); + throw error; + } + + return response; } }