1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 09:13:33 +00:00

[EC-598] feat: rudimentar error handling

This commit is contained in:
Andreas Coroiu
2022-12-16 15:01:49 +01:00
parent ef1380cb24
commit 7630ce881c
9 changed files with 70 additions and 11 deletions

View File

@@ -209,9 +209,17 @@ export default class RuntimeBackground {
this.platformUtilsService.copyToClipboard(msg.identifier, { window: window }); this.platformUtilsService.copyToClipboard(msg.identifier, { window: window });
break; break;
case "fido2RegisterCredentialRequest": case "fido2RegisterCredentialRequest":
return await this.main.fido2Service.createCredential(msg.data); try {
return { result: await this.main.fido2Service.createCredential(msg.data) };
} catch (error) {
return { error: { ...error, message: error.message } };
}
case "fido2GetCredentialRequest": case "fido2GetCredentialRequest":
return await this.main.fido2Service.assertCredential(msg.data); try {
return { result: await this.main.fido2Service.assertCredential(msg.data) };
} catch (error) {
return { error: { ...error, message: error.message } };
}
} }
return undefined; return undefined;
} }

View File

@@ -21,8 +21,8 @@ messenger.addHandler(async (message) => {
(response) => { (response) => {
resolve({ resolve({
type: MessageType.CredentialCreationResponse, type: MessageType.CredentialCreationResponse,
approved: true, result: response.result,
result: response, error: response.error,
}); });
} }
); );
@@ -39,7 +39,8 @@ messenger.addHandler(async (message) => {
(response) => { (response) => {
resolve({ resolve({
type: MessageType.CredentialGetResponse, type: MessageType.CredentialGetResponse,
result: response, result: response.result,
error: response.error,
}); });
} }
); );

View File

@@ -0,0 +1,10 @@
export enum MessageErrorType {
RequestAborted,
}
export type RequestAbortedMessageError = {
fallbackRequested: boolean;
message: string;
};
export type MessageError = RequestAbortedMessageError;

View File

@@ -5,6 +5,8 @@ import {
CredentialRegistrationResult, CredentialRegistrationResult,
} from "@bitwarden/common/abstractions/fido2/fido2.service.abstraction"; } from "@bitwarden/common/abstractions/fido2/fido2.service.abstraction";
import { MessageError } from "./error";
export enum MessageType { export enum MessageType {
CredentialCreationRequest, CredentialCreationRequest,
CredentialCreationResponse, CredentialCreationResponse,
@@ -21,8 +23,8 @@ export type CredentialCreationRequest = {
export type CredentialCreationResponse = { export type CredentialCreationResponse = {
type: MessageType.CredentialCreationResponse; type: MessageType.CredentialCreationResponse;
approved: boolean;
result?: CredentialRegistrationResult; result?: CredentialRegistrationResult;
error?: MessageError;
}; };
export type CredentialGetRequest = { export type CredentialGetRequest = {
@@ -33,6 +35,7 @@ export type CredentialGetRequest = {
export type CredentialGetResponse = { export type CredentialGetResponse = {
type: MessageType.CredentialGetResponse; type: MessageType.CredentialGetResponse;
result?: CredentialAssertResult; result?: CredentialAssertResult;
error?: MessageError;
}; };
export type AbortRequest = { export type AbortRequest = {

View File

@@ -19,10 +19,18 @@ navigator.credentials.create = async (options?: CredentialCreationOptions): Prom
data: WebauthnUtils.mapCredentialCreationOptions(options, window.location.origin), data: WebauthnUtils.mapCredentialCreationOptions(options, window.location.origin),
}); });
if (response.type !== MessageType.CredentialCreationResponse || !response.approved) { if (response.type !== MessageType.CredentialCreationResponse) {
return await browserCredentials.create(options); return await browserCredentials.create(options);
} }
if (response.error && response.error.fallbackRequested) {
return await browserCredentials.create(options);
}
if (response.error) {
throw new Error(response.error.message ?? "The request was aborted.");
}
return WebauthnUtils.mapCredentialRegistrationResult(response.result); return WebauthnUtils.mapCredentialRegistrationResult(response.result);
}; };
@@ -36,5 +44,13 @@ navigator.credentials.get = async (options?: CredentialRequestOptions): Promise<
return await browserCredentials.get(options); return await browserCredentials.get(options);
} }
if (response.error && response.error.fallbackRequested) {
return await browserCredentials.create(options);
}
if (response.error) {
throw new Error(response.error.message ?? "The request was aborted.");
}
return WebauthnUtils.mapCredentialAssertResult(response.result); return WebauthnUtils.mapCredentialAssertResult(response.result);
}; };

View File

@@ -9,5 +9,8 @@
<ng-container *ngIf="data.type == 'VerifyUserRequest'">Authenticate</ng-container> <ng-container *ngIf="data.type == 'VerifyUserRequest'">Authenticate</ng-container>
<ng-container *ngIf="data.type == 'ConfirmNewCredentialRequest'">Create</ng-container> <ng-container *ngIf="data.type == 'ConfirmNewCredentialRequest'">Create</ng-container>
</button> </button>
<button type="button" class="btn btn-outline-secondary" (click)="cancel()">Cancel</button> <button type="button" class="btn btn-outline-secondary" (click)="cancel(true)">
Use browser built-in
</button>
<button type="button" class="btn btn-outline-secondary" (click)="cancel(false)">Abort</button>
</div> </div>

View File

@@ -35,23 +35,25 @@ export class Fido2Component {
BrowserFido2UserInterfaceService.sendMessage({ BrowserFido2UserInterfaceService.sendMessage({
requestId: data.requestId, requestId: data.requestId,
type: "RequestCancelled", type: "RequestCancelled",
fallbackRequested: true,
}); });
} }
window.close(); window.close();
} }
cancel() { cancel(fallback: boolean) {
this.unload(); this.unload(fallback);
window.close(); window.close();
} }
@HostListener("window:unload") @HostListener("window:unload")
unload() { unload(fallback = true) {
const data = this.data; const data = this.data;
BrowserFido2UserInterfaceService.sendMessage({ BrowserFido2UserInterfaceService.sendMessage({
requestId: data.requestId, requestId: data.requestId,
type: "RequestCancelled", type: "RequestCancelled",
fallbackRequested: fallback,
}); });
} }
} }

View File

@@ -3,6 +3,7 @@ import { filter, first, lastValueFrom, Subject, takeUntil } from "rxjs";
import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/abstractions/fido2/fido2-user-interface.service.abstraction"; import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/abstractions/fido2/fido2-user-interface.service.abstraction";
import { Utils } from "@bitwarden/common/misc/utils"; import { Utils } from "@bitwarden/common/misc/utils";
import { RequestAbortedError } from "../../../../../libs/common/src/abstractions/fido2/fido2.service.abstraction";
import { BrowserApi } from "../../browser/browserApi"; import { BrowserApi } from "../../browser/browserApi";
import { PopupUtilsService } from "../../popup/services/popup-utils.service"; import { PopupUtilsService } from "../../popup/services/popup-utils.service";
@@ -23,6 +24,7 @@ export type BrowserFido2Message = { requestId: string } & (
} }
| { | {
type: "RequestCancelled"; type: "RequestCancelled";
fallbackRequested: boolean;
} }
); );
@@ -68,6 +70,10 @@ export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServi
return true; return true;
} }
if (response.type === "RequestCancelled") {
throw new RequestAbortedError(response.fallbackRequested);
}
return false; return false;
} }
@@ -93,6 +99,10 @@ export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServi
return true; return true;
} }
if (response.type === "RequestCancelled") {
throw new RequestAbortedError(response.fallbackRequested);
}
return false; return false;
} }

View File

@@ -54,6 +54,12 @@ export interface CredentialAssertResult {
userHandle: string; userHandle: string;
} }
export class RequestAbortedError extends Error {
constructor(readonly fallbackRequested = false) {
super("Fido2 request was aborted");
}
}
export abstract class Fido2Service { export abstract class Fido2Service {
createCredential: (params: CredentialRegistrationParams) => Promise<CredentialRegistrationResult>; createCredential: (params: CredentialRegistrationParams) => Promise<CredentialRegistrationResult>;
assertCredential: (params: CredentialAssertParams) => Promise<CredentialAssertResult>; assertCredential: (params: CredentialAssertParams) => Promise<CredentialAssertResult>;