Add support for no intrusive biometric (#2431)
This commit is contained in:
parent
20f475be62
commit
d5334360dd
|
@ -1494,7 +1494,7 @@
|
|||
"message": "Start the Bitwarden Desktop application"
|
||||
},
|
||||
"startDesktopDesc": {
|
||||
"message": "The Bitwarden Desktop application needs to be started before this function can be used."
|
||||
"message": "The Bitwarden Desktop application needs to be started before unlock with biometrics can be used."
|
||||
},
|
||||
"errorEnableBiometricTitle": {
|
||||
"message": "Unable to enable biometrics"
|
||||
|
@ -1884,5 +1884,8 @@
|
|||
"example": "name@example.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"message": "Error"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,13 +115,7 @@ export class NativeMessagingBackground {
|
|||
break;
|
||||
case "disconnected":
|
||||
if (this.connecting) {
|
||||
this.messagingService.send("showDialog", {
|
||||
text: this.i18nService.t("startDesktopDesc"),
|
||||
title: this.i18nService.t("startDesktopTitle"),
|
||||
confirmText: this.i18nService.t("ok"),
|
||||
type: "error",
|
||||
});
|
||||
reject();
|
||||
reject("startDesktop");
|
||||
}
|
||||
this.connected = false;
|
||||
this.port.disconnect();
|
||||
|
@ -192,18 +186,12 @@ export class NativeMessagingBackground {
|
|||
error = chrome.runtime.lastError.message;
|
||||
}
|
||||
|
||||
if (error != null) {
|
||||
this.messagingService.send("showDialog", {
|
||||
text: this.i18nService.t("desktopIntegrationDisabledDesc"),
|
||||
title: this.i18nService.t("desktopIntegrationDisabledTitle"),
|
||||
confirmText: this.i18nService.t("ok"),
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
this.sharedSecret = null;
|
||||
this.privateKey = null;
|
||||
this.connected = false;
|
||||
reject();
|
||||
|
||||
const reason = error != null ? "desktopIntegrationDisabled" : null;
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
type BiometricError = {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type BiometricErrorTypes = "startDesktop" | "desktopIntegrationDisabled";
|
||||
|
||||
export const BiometricErrors: Record<BiometricErrorTypes, BiometricError> = {
|
||||
startDesktop: {
|
||||
title: "startDesktopTitle",
|
||||
description: "startDesktopDesc",
|
||||
},
|
||||
desktopIntegrationDisabled: {
|
||||
title: "desktopIntegrationDisabledTitle",
|
||||
description: "desktopIntegrationDisabledDesc",
|
||||
},
|
||||
};
|
|
@ -62,7 +62,13 @@
|
|||
</div>
|
||||
<div class="box" *ngIf="biometricLock">
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn primary block" (click)="unlockBiometric()" appStopClick>
|
||||
<button
|
||||
type="button"
|
||||
class="btn primary block"
|
||||
(click)="unlockBiometric()"
|
||||
appStopClick
|
||||
[disabled]="pendingBiometric"
|
||||
>
|
||||
{{ "unlockWithBiometrics" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -71,5 +77,9 @@
|
|||
<button type="button" appStopClick (click)="logOut()">{{ "logOut" | i18n }}</button>
|
||||
</p>
|
||||
<app-private-mode-warning></app-private-mode-warning>
|
||||
<app-callout *ngIf="biometricError" type="error">{{ biometricError }}</app-callout>
|
||||
<p class="text-center text-muted" *ngIf="pendingBiometric">
|
||||
<i class="bwi bwi-spinner bwi-spin" aria-hidden="true"></i> {{ "awaitDesktop" | i18n }}
|
||||
</p>
|
||||
</content>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Component, NgZone } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
|
@ -14,6 +13,8 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
|
|||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { BiometricErrors, BiometricErrorTypes } from "../../models/biometricErrors";
|
||||
|
||||
@Component({
|
||||
selector: "app-lock",
|
||||
templateUrl: "lock.component.html",
|
||||
|
@ -21,6 +22,9 @@ import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.serv
|
|||
export class LockComponent extends BaseLockComponent {
|
||||
private isInitialLockScreen: boolean;
|
||||
|
||||
biometricError: string;
|
||||
pendingBiometric = false;
|
||||
|
||||
constructor(
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
|
@ -73,25 +77,23 @@ export class LockComponent extends BaseLockComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.innerHTML = `<div class="swal2-text">${this.i18nService.t("awaitDesktop")}</div>`;
|
||||
this.pendingBiometric = true;
|
||||
this.biometricError = null;
|
||||
|
||||
Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
html: div,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t("cancel"),
|
||||
showConfirmButton: false,
|
||||
});
|
||||
let success;
|
||||
try {
|
||||
success = await super.unlockBiometric();
|
||||
} catch (e) {
|
||||
const error = BiometricErrors[e as BiometricErrorTypes];
|
||||
|
||||
const success = await super.unlockBiometric();
|
||||
|
||||
// Avoid closing the error dialogs
|
||||
if (success) {
|
||||
Swal.close();
|
||||
if (error == null) {
|
||||
this.logService.error("Unknown error: " + e);
|
||||
}
|
||||
|
||||
this.biometricError = this.i18nService.t(error.description);
|
||||
}
|
||||
this.pendingBiometric = false;
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, NgZone, OnInit, SecurityContext } from "@
|
|||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { NavigationEnd, Router, RouterOutlet } from "@angular/router";
|
||||
import { IndividualConfig, ToastrService } from "ngx-toastr";
|
||||
import Swal, { SweetAlertIcon } from "sweetalert2/src/sweetalert2.js";
|
||||
import Swal, { SweetAlertIcon } from "sweetalert2";
|
||||
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@import "~ngx-toastr/toastr";
|
||||
@import "~sweetalert2/src/sweetalert2.scss";
|
||||
@import "~#sweetalert2";
|
||||
|
||||
@import "variables.scss";
|
||||
@import "buttons.scss";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, ElementRef, OnInit, ViewChild } from "@angular/core";
|
||||
import { FormControl } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
import Swal from "sweetalert2/src/sweetalert2.js";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
|
@ -15,6 +15,7 @@ import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.serv
|
|||
import { DeviceType } from "jslib-common/enums/deviceType";
|
||||
|
||||
import { BrowserApi } from "../../browser/browserApi";
|
||||
import { BiometricErrors, BiometricErrorTypes } from "../../models/biometricErrors";
|
||||
import { SetPinComponent } from "../components/set-pin.component";
|
||||
import { PopupUtilsService } from "../services/popup-utils.service";
|
||||
|
||||
|
@ -269,6 +270,16 @@ export class SettingsComponent implements OnInit {
|
|||
.catch((e) => {
|
||||
// Handle connection errors
|
||||
this.biometric = false;
|
||||
|
||||
const error = BiometricErrors[e as BiometricErrorTypes];
|
||||
|
||||
this.platformUtilsService.showDialog(
|
||||
this.i18nService.t(error.description),
|
||||
this.i18nService.t(error.title),
|
||||
this.i18nService.t("ok"),
|
||||
null,
|
||||
"error"
|
||||
);
|
||||
}),
|
||||
]);
|
||||
} else {
|
||||
|
|
|
@ -168,6 +168,10 @@ const config = {
|
|||
extensions: [".ts", ".js"],
|
||||
symlinks: false,
|
||||
modules: [path.resolve("node_modules")],
|
||||
alias: {
|
||||
sweetalert2: require.resolve("sweetalert2/dist/sweetalert2.js"),
|
||||
"#sweetalert2": require.resolve("sweetalert2/src/sweetalert2.scss"),
|
||||
},
|
||||
fallback: {
|
||||
assert: false,
|
||||
buffer: require.resolve("buffer/"),
|
||||
|
|
Loading…
Reference in New Issue