1
0

feat(BRIDGE-424): FIDO2 GUI support.

This commit is contained in:
Atanas Janeshliev
2025-09-16 13:07:45 +02:00
parent e091e58be1
commit edf903fd21
42 changed files with 3567 additions and 3510 deletions

View File

@ -863,6 +863,12 @@ void QMLBackend::login2FA(QString const &username, QString const &code) const {
)
}
void QMLBackend::loginFido(const QString &username, QString const &pin) const {
HANDLE_EXCEPTION(
app().grpc().loginFido(username, pin);
)
}
//****************************************************************************************************************************************************
/// \param[in] username The username.
@ -884,6 +890,11 @@ void QMLBackend::loginAbort(QString const &username) const {
)
}
void QMLBackend::abortFidoAssertion(QString const &username) const {
HANDLE_EXCEPTION(
app().grpc().abortFidoAssertion(username);
)
}
//****************************************************************************************************************************************************
/// \param[in] active Should DoH be active.
@ -1206,7 +1217,6 @@ void QMLBackend::onLoginAlreadyLoggedIn(QString const &userID) {
)
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
//****************************************************************************************************************************************************
@ -1347,11 +1357,19 @@ void QMLBackend::connectGrpcEvents() {
connect(client, &GRPCClient::login2FARequested, this, &QMLBackend::login2FARequested);
connect(client, &GRPCClient::login2FAError, this, &QMLBackend::login2FAError);
connect(client, &GRPCClient::login2FAErrorAbort, this, &QMLBackend::login2FAErrorAbort);
connect(client, &GRPCClient::loginFidoRequested, this, &QMLBackend::loginFidoRequested);
connect(client, &GRPCClient::login2FAOrFidoRequested, this, &QMLBackend::login2FAOrFidoRequested);
connect(client, &GRPCClient::login2PasswordRequested, this, &QMLBackend::login2PasswordRequested);
connect(client, &GRPCClient::login2PasswordError, this, &QMLBackend::login2PasswordError);
connect(client, &GRPCClient::login2PasswordErrorAbort, this, &QMLBackend::login2PasswordErrorAbort);
connect(client, &GRPCClient::loginFinished, this, &QMLBackend::onLoginFinished);
connect(client, &GRPCClient::loginAlreadyLoggedIn, this, &QMLBackend::onLoginAlreadyLoggedIn);
connect(client, &GRPCClient::loginFidoTouchRequested, this, &QMLBackend::loginFidoTouchRequested);
connect(client, &GRPCClient::loginFidoTouchCompleted, this, &QMLBackend::loginFidoTouchCompleted);
connect(client, &GRPCClient::loginFidoPinRequired, this, &QMLBackend::loginFidoPinRequired);
connect(client, &GRPCClient::loginFidoPinInvalid, this, &QMLBackend::loginFidoPinInvalid);
connect(client, &GRPCClient::loginFidoPinBlocked, this, &QMLBackend::loginFidoPinBlocked);
connect(client, &GRPCClient::loginFidoError, this, &QMLBackend::loginFidoError);
connect(client, &GRPCClient::loginHvRequested, this, &QMLBackend::loginHvRequested);
connect(client, &GRPCClient::loginHvError, this, &QMLBackend::loginHvError);

View File

@ -193,8 +193,10 @@ public slots: // slot for signals received from QML -> To be forwarded to Bridge
void login(QString const &username, QString const &password) const; ///< Slot for the login button (initial login).
void loginHv(QString const &username, QString const &password) const; ///< Slot for the login button (after HV challenge completed).
void login2FA(QString const &username, QString const &code) const; ///< Slot for the login button (2FA login).
void loginFido(QString const &username, QString const &pin) const; ///< Slot for the authenticate button (FIDO2/Security Key login).
void login2Password(QString const &username, QString const &password) const; ///< Slot for the login button (mailbox password login).
void loginAbort(QString const &username) const; ///< Slot for the login abort procedure.
void abortFidoAssertion(QString const &username) const; ///< Slot for aborting the FIDO login procedure.
void toggleDoH(bool active); ///, Slot for the DoH toggle.
void toggleAutomaticUpdate(bool makeItActive); ///< Slot for the automatic update toggle
void updateCurrentMailClient(); ///< Slot for the change of the current mail client.
@ -242,11 +244,19 @@ signals: // Signals received from the Go backend, to be forwarded to QML
void login2FARequested(QString const &username); ///< Signal for the 'login2FARequested' gRPC stream event.
void login2FAError(QString const &errorMsg); ///< Signal for the 'login2FAError' gRPC stream event.
void login2FAErrorAbort(QString const &errorMsg); ///< Signal for the 'login2FAErrorAbort' gRPC stream event.
void loginFidoRequested(QString const &username); ///< Signal for the 'loginFidoRequested' gRPC stream event.
void login2FAOrFidoRequested(QString const &username); ///<Signal for the 'login2FAOrFidoRequested' gRPC stream event.
void login2PasswordRequested(QString const &username); ///< Signal for the 'login2PasswordRequested' gRPC stream event.
void login2PasswordError(QString const &errorMsg); ///< Signal for the 'login2PasswordError' gRPC stream event.
void login2PasswordErrorAbort(QString const &errorMsg); ///< Signal for the 'login2PasswordErrorAbort' gRPC stream event.
void loginFinished(int index, bool wasSignedOut); ///< Signal for the 'loginFinished' gRPC stream event.
void loginAlreadyLoggedIn(int index); ///< Signal for the 'loginAlreadyLoggedIn' gRPC stream event.
void loginFidoTouchRequested(QString const &username); ///< Signal for the `loginFidoTouchRequested' gRPC stream event.
void loginFidoTouchCompleted(QString const &username); ///< Signal for the `loginFidoTouchCompleted' gRPC stream event.
void loginFidoPinRequired(QString const &username); ///< Signal for the `loginFidoPinRequired' gRPC stream event.
void loginFidoPinInvalid(QString const &errorMsg); ///< Signal for the `loginFidoPinInvalid' gRPC stream event.
void loginFidoPinBlocked(QString const &errorMsg); ///< Signal for the `loginFidoPinBlocked' gRPC stream event.
void loginFidoError(QString const &errorMsg); ///< Signal for the 'loginFidoError' gRPC stream event.
void loginHvRequested(QString const &hvUrl); ///< Signal for the 'loginHvRequested' gRPC stream event.
void loginHvError(QString const &errorMsg); ///< Signal for the 'loginHvError' gRPC stream event.
void updateManualReady(QString const &version); ///< Signal for the 'updateManualReady' gRPC stream event.

View File

@ -72,6 +72,7 @@
<file>qml/icons/systray-mono-warn.png</file>
<file>qml/icons/systray.svg</file>
<file>qml/icons/ic-notification-bell.svg</file>
<file>qml/icons/fingerprint.svg</file>
<file alias="bridge.svg">../../../../dist/bridge.svg</file>
<file alias="bridgeMacOS.svg">../../../../dist/bridgeMacOS.svg</file>
<file>qml/KeychainSettings.qml</file>
@ -112,6 +113,7 @@
<file>qml/Proton/TextArea.qml</file>
<file>qml/Proton/TextField.qml</file>
<file>qml/Proton/Toggle.qml</file>
<file>qml/Proton/Spinner.qml</file>
<file>qml/Resources/bug_report_flow.json</file>
<file>qml/Resources/Help/Template.html</file>
<file>qml/Resources/Help/WhyBridge.html</file>

View File

@ -22,6 +22,7 @@ Write-host "Bridge-gui directory is $scriptDir"
Write-host "Bridge repos root dir $bridgeRepoRootDir"
Push-Location $scriptDir
$ErrorActionPreference = "Stop"
$cmakeExe=$(Get-Command cmake).source

View File

@ -24,6 +24,9 @@ Dialog {
property var notification
property bool isUserNotification: false
// Placeholder for text input label text.
property string textFieldText: ""
modal: true
shouldShow: notification && notification.active && !notification.dismissed
@ -53,6 +56,7 @@ Dialog {
sourceSize.width: 64
visible: source != ""
}
Label {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 8
@ -83,6 +87,43 @@ Dialog {
implicitWidth: additionalChildrenContainer.childrenRect.width
visible: children.length > 0
}
Image {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 16
Layout.preferredHeight: 64
Layout.preferredWidth: 64
source: root.notification.additionalImageSrc
sourceSize.height: 64
sourceSize.width: 64
visible: root.notification.additionalImageSrc != ""
}
TextField {
id: textField
Layout.fillWidth: true
Layout.preferredWidth: 240
Layout.bottomMargin: 16
colorScheme: root.colorScheme
text: root.textFieldText
visible: root.notification && root.notification.useTextField
onTextChanged: root.notification.textFieldChanged(text)
Connections {
target: root.notification
function onClearTextFieldRequested() {
root.notification.textFieldChanged("")
textField.clear();
}
function onFocusTextField() {
textField.focus = true;
}
}
}
LinkLabel {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 32
@ -94,6 +135,18 @@ Dialog {
}
Spinner {
Layout.alignment: Qt.AlignHCenter
colorScheme: root.colorScheme
Layout.bottomMargin: 16
Layout.preferredHeight: 64
Layout.preferredWidth: 64
size: 64
running: true
visible: root.notification && root.notification.busyIndicator
}
ColumnLayout {
spacing: 8
@ -105,8 +158,7 @@ Dialog {
action: modelData
colorScheme: root.colorScheme
loading: modelData.loading
secondary: index > 0
}
secondary: modelData.forceSecondary !== undefined ? modelData.forceSecondary : index > 0 }
}
}
}

View File

@ -109,6 +109,18 @@ Item {
colorScheme: root.colorScheme
notification: root.notifications.repairBridge
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.touchFidoKey
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.fidoPinRequested
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.fidoPinBlocked
}
UserNotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.userNotification

View File

@ -40,6 +40,19 @@ QtObject {
property string subtitle
property string username
// Whether to display a spinner.
property bool busyIndicator: false
// Whether to display a text input field.
property bool useTextField: false
// Source for an additional image, won't be displayed if empty.
property string additionalImageSrc: ""
// Text input field operations via signals.
signal clearTextFieldRequested()
signal textFieldChanged(string value)
signal focusTextField()
onActiveChanged: {
dismissed = false;

View File

@ -62,7 +62,7 @@ QtObject {
target: Backend
}
}
property var all: [root.noInternet, root.imapPortStartupError, root.smtpPortStartupError, root.imapPortChangeError, root.smtpPortChangeError, root.imapConnectionModeChangeError, root.smtpConnectionModeChangeError, root.updateManualReady, root.updateManualRestartNeeded, root.updateManualError, root.updateForce, root.updateForceError, root.updateSilentRestartNeeded, root.updateSilentError, root.updateIsLatestVersion, root.loginConnectionError, root.onlyPaidUsers, root.alreadyLoggedIn, root.enableBeta, root.bugReportSendSuccess, root.bugReportSendError, root.bugReportSendFallback, root.cacheCantMove, root.cacheLocationChangeSuccess, root.enableSplitMode, root.resetBridge, root.changeAllMailVisibility, root.deleteAccount, root.noKeychain, root.rebuildKeychain, root.addressChanged, root.apiCertIssue, root.userBadEvent, root.imapLoginWhileSignedOut, root.genericError, root.genericQuestion, root.hvErrorEvent, root.repairBridge, root.userNotification]
property var all: [root.noInternet, root.imapPortStartupError, root.smtpPortStartupError, root.imapPortChangeError, root.smtpPortChangeError, root.imapConnectionModeChangeError, root.smtpConnectionModeChangeError, root.updateManualReady, root.updateManualRestartNeeded, root.updateManualError, root.updateForce, root.updateForceError, root.updateSilentRestartNeeded, root.updateSilentError, root.updateIsLatestVersion, root.loginConnectionError, root.onlyPaidUsers, root.alreadyLoggedIn, root.enableBeta, root.bugReportSendSuccess, root.bugReportSendError, root.bugReportSendFallback, root.cacheCantMove, root.cacheLocationChangeSuccess, root.enableSplitMode, root.resetBridge, root.changeAllMailVisibility, root.deleteAccount, root.noKeychain, root.rebuildKeychain, root.addressChanged, root.apiCertIssue, root.userBadEvent, root.imapLoginWhileSignedOut, root.genericError, root.genericQuestion, root.hvErrorEvent, root.repairBridge, root.userNotification, root.touchFidoKey, root.fidoPinRequested, root.fidoPinBlocked, root.fidoErrorEvent]
property Notification alreadyLoggedIn: Notification {
brief: qsTr("Already signed in")
description: qsTr("This account is already signed in.")
@ -1229,6 +1229,170 @@ QtObject {
}
}
property Notification touchFidoKey: Notification {
title: qsTr("Touch your security key")
description: qsTr("To complete authentication, touch the button or sensor on your security key.")
group: Notifications.Group.Dialogs
icon: "./icons/ic-exclamation-circle-filled.svg"
type: Notification.NotificationType.Info
additionalImageSrc: "./icons/fingerprint.svg"
function reset() {
root.touchFidoKey.active = false;
root.touchFidoKey.busyIndicator = false;
root.touchFidoKey.additionalImageSrc = "./icons/fingerprint.svg";
}
action: [
Action {
id: touchFidoKey_cancel
text: qsTr("Cancel")
property bool forceSecondary: true
onTriggered: {
Backend.abortFidoAssertion(root.touchFidoKey.username);
root.touchFidoKey.reset();
}
}
]
Connections {
function onLoginFidoTouchRequested(username) {
root.touchFidoKey.username = username;
root.touchFidoKey.active = true;
touchFidoKey_cancel.enabled = true;
}
function onLoginFidoTouchCompleted(_) {
root.touchFidoKey.additionalImageSrc = "";
root.touchFidoKey.busyIndicator = true;
touchFidoKey_cancel.enabled = false;
}
function onLoginFidoPinInvalid(_) {
root.touchFidoKey.reset();
}
function onLoginFinished(_) {
root.touchFidoKey.reset();
}
function onLoginFidoError(errorMsg) {
root.touchFidoKey.reset();
}
target: Backend
}
}
property Notification fidoPinRequested: Notification {
property string fidoPinInput: ""
title: qsTr("Enter security key PIN")
description: qsTr("To continue, enter the PIN for your security key.")
group: Notifications.Group.Dialogs
icon: "./icons/ic-exclamation-circle-filled.svg"
type: Notification.NotificationType.Info
useTextField: true
onTextFieldChanged: function(value) {
root.fidoPinRequested.fidoPinInput = value;
}
function reset() {
root.fidoPinRequested.active = false;
root.fidoPinRequested.clearTextFieldRequested();
root.fidoPinRequested.type = Notification.NotificationType.Info;
}
function clearAndFocusTextField() {
root.fidoPinRequested.clearTextFieldRequested();
root.fidoPinRequested.focusTextField()
}
action: [
Action {
text: qsTr("Continue")
onTriggered: {
Backend.loginFido("", Qt.btoa(root.fidoPinRequested.fidoPinInput));
root.fidoPinRequested.reset();
}
},
Action {
text: qsTr("Cancel")
onTriggered: {
root.fidoPinRequested.reset();
}
}
]
Connections {
function onLoginFidoPinRequired(_) {
root.fidoPinRequested.clearAndFocusTextField();
root.fidoPinRequested.active = true;
}
function onLoginFidoPinInvalid(_) {
root.fidoPinRequested.clearAndFocusTextField();
root.fidoPinRequested.active = true;
root.fidoPinRequested.description = qsTr("The PIN you entered is incorrect. Try again.");
root.fidoPinRequested.type = Notification.NotificationType.Warning;
}
function onLoginFidoTouchRequested(_) {
root.fidoPinRequested.reset();
}
function onLoginFinished(_) {
root.fidoPinRequested.reset();
}
function onLoginFidoError(errorMsg) {
root.fidoPinRequested.reset();
}
target: Backend
}
}
property Notification fidoPinBlocked: Notification {
title: qsTr("Security key PIN blocked")
description: qsTr("Your security key PIN is blocked due to too many failed attempts. Try removing and re-inserting your key, or check your security key's documentation for unlock instructions.")
group: Notifications.Group.Dialogs
icon: "./icons/ic-exclamation-circle-filled.svg"
type: Notification.NotificationType.Danger
action: [
Action {
text: qsTr("OK")
onTriggered: {
root.fidoPinBlocked.active = false;
root.touchFidoKey.reset();
root.fidoPinRequested.reset();
}
}
]
Connections {
function onLoginFidoPinBlocked(_) {
root.fidoPinBlocked.active = true;
}
target: Backend
}
}
property Notification fidoErrorEvent: Notification {
group: Notifications.Group.Configuration
icon: "./icons/ic-exclamation-circle-filled.svg"
type: Notification.NotificationType.Danger
action: Action {
text: qsTr("OK")
onTriggered: {
root.fidoErrorEvent.active = false;
}
}
Connections {
function onLoginFidoError(errorMsg) {
root.fidoErrorEvent.active = true;
root.fidoErrorEvent.description = errorMsg;
}
target: Backend
}
}
signal askChangeAllMailVisibility(var isVisibleNow)
signal askDeleteAccount(var user)
signal askEnableBeta

View File

@ -0,0 +1,52 @@
// Copyright (c) 2025 Proton AG
// This file is part of Proton Mail Bridge.
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
import QtQuick
import QtQuick.Controls.impl
Item {
id: root
property ColorScheme colorScheme: ProtonStyle.currentStyle
property color color: colorScheme.interaction_norm
property int size: 16
property bool running: true
property int duration: 1000
property string source: "/qml/icons/Loader_48.svg"
implicitWidth: size
implicitHeight: size
ColorImage {
id: spinnerImage
anchors.centerIn: parent
width: root.size
height: root.size
source: root.source
color: root.color
sourceSize.width: root.size
sourceSize.height: root.size
visible: root.running
RotationAnimation {
target: spinnerImage
property: "rotation"
from: 0
to: 360
duration: root.duration
loops: Animation.Infinite
running: root.running
direction: RotationAnimation.Clockwise
}
}
}

View File

@ -40,3 +40,4 @@ TextField 4.0 TextField.qml
Toggle 4.0 Toggle.qml
WebFrame 4.0 WebFrame.qml
ContextMenu 4.0 ContextMenu.qml
Spinner 1.0 Spinner.qml

View File

@ -21,6 +21,8 @@ FocusScope {
enum RootStack {
Login,
TOTP,
FIDO,
TOTPOrFIDO,
MailboxPassword,
HV
}
@ -51,6 +53,7 @@ FocusScope {
passwordTextField.hidePassword();
secondPasswordTextField.hidePassword();
hvLinkClicked = false;
fidoLayout.reset();
}
function resetViaHv() {
usernameTextField.enabled = false;
@ -93,6 +96,29 @@ FocusScope {
twoFactorUsernameLabel.text = username;
stackLayout.currentIndex = Login.RootStack.TOTP;
twoFactorPasswordTextField.focus = true;
switchToTotpButton.visible = false;
switchToFidoButton.visible = false;
}
function onLoginFidoRequested(username) {
fidoUsernameLabel.text = username;
stackLayout.currentIndex = Login.RootStack.FIDO;
switchToTotpButton.visible = false;
switchToFidoButton.visible = false;
}
function onLogin2FAOrFidoRequested(username) {
fidoUsernameLabel.text = username;
twoFactorUsernameLabel.text = username;
stackLayout.currentIndex = Login.RootStack.FIDO;
switchToTotpButton.visible = true;
switchToFidoButton.visible = true;
}
function onLoginFidoPinBlocked(_) {
console.assert(stackLayout.currentIndex === Login.RootStack.FIDO, "Unexpected onLoginFidoPinBlocked");
root.reset();
}
function onLoginFidoError(_) {
console.assert(stackLayout.currentIndex === Login.RootStack.FIDO || stackLayout.currentIndex === Login.RootStack.Login, "Unexpected loginFidoError");
root.reset();
}
function onLogin2PasswordError(_) {
console.assert(stackLayout.currentIndex === Login.RootStack.MailboxPassword, "Unexpected login2PasswordError");
@ -352,7 +378,7 @@ FocusScope {
Layout.fillWidth: true
colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: qsTr("You have enabled two-factor authentication. Please enter the 6-digit code provided by your authenticator application.")
text: qsTr("You have enabled two-factor authentication. Enter the 6-digit code provided by your authenticator application.")
type: Label.LabelType.Body
wrapMode: Text.WordWrap
}
@ -406,6 +432,117 @@ FocusScope {
root.abort();
}
}
Label {
id: switchToFidoButton
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: "<a href='#'>" + qsTr("Use security key instead") + "</a>"
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: !twoFAButton.loading
onClicked: {
stackLayout.currentIndex = Login.RootStack.FIDO;
fidoLayout.reset();
totpLayout.reset();
}
}
}
}
}
Item {
ColumnLayout {
id: fidoLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: ProtonStyle.wizard_spacing_medium
function reset() {
fidoButton.loading = false;
}
ColumnLayout {
Layout.fillWidth: true
spacing: ProtonStyle.wizard_spacing_small
Label {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: qsTr("Security key authentication")
type: Label.LabelType.Title
}
Label {
id: fidoUsernameLabel
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
color: wizard.colorScheme.text_weak
colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: ""
type: Label.LabelType.Body
}
}
Label {
id: fidoDescriptionLabel
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: qsTr("Security key authentication is enabled. Please connect your security key.")
type: Label.LabelType.Body
wrapMode: Text.WordWrap
}
Button {
id: fidoButton
Layout.fillWidth: true
colorScheme: wizard.colorScheme
enabled: !loading
text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
onClicked: {
if (Backend.goos === "windows") {
fidoButton.loading = true;
}
Backend.loginFido(usernameTextField.text, "");
}
}
Button {
Layout.fillWidth: true
colorScheme: wizard.colorScheme
enabled: !fidoButton.loading
secondary: true
secondaryIsOpaque: true
text: qsTr("Cancel")
onClicked: {
root.abort();
}
}
Label {
id: switchToTotpButton
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
colorScheme: wizard.colorScheme
horizontalAlignment: Text.AlignHCenter
text: "<a href='#'>" + qsTr("Use authenticator app instead") + "</a>"
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: !fidoButton.loading
onClicked: {
stackLayout.currentIndex = Login.RootStack.TOTP;
fidoLayout.reset();
totpLayout.reset();
}
}
}
}
}
Item {
@ -499,7 +636,9 @@ FocusScope {
text: qsTr("Cancel")
onClicked: {
root.abort();
stackLayout.currentIndex = Login.RootStack.TOTP;
twoFactorPasswordTextField.focus = true;
}
}
}

View File

@ -0,0 +1,4 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.6112 6.03055C13.016 5.38841 3.66669 15.003 4.55919 26.5258C4.62317 27.3517 4.00546 28.0732 3.17951 28.1371C2.35356 28.2011 1.63213 27.5834 1.56815 26.7574C0.53451 13.4124 11.3953 2.26091 24.8337 3.03834L24.8838 3.04124L25.8497 3.16232C36.7014 3.83482 45.4565 12.5137 46.3592 23.3767L46.3613 23.4014L46.4808 25.2023L46.4812 25.2082C46.7848 29.3648 43.405 32.765 39.3857 32.61L39.3601 32.609C36.569 32.4535 34.1159 30.5901 33.2016 27.8396L33.1927 27.8128L31.9883 23.9472L31.9864 23.9409C30.7522 19.9188 26.8518 17.3489 22.6425 17.8625C18.5134 18.3812 15.2451 21.8004 15.0421 25.8707L15.0412 25.8894C14.8217 29.4114 15.687 34.3093 19.2905 40.3707C19.7138 41.0828 19.4797 42.0032 18.7676 42.4265C18.0555 42.8499 17.1351 42.6158 16.7118 41.9037C12.8494 35.4069 11.7861 29.9196 12.0463 25.7119C12.3297 20.1236 16.7679 15.5754 22.2717 14.8856L22.2762 14.885C27.9429 14.192 33.1957 17.6603 34.8535 23.0578L34.8544 23.0609L36.0526 26.9056C36.5854 28.4923 37.9777 29.5218 39.5142 29.6127C41.7516 29.6914 43.6632 27.7826 43.4889 25.4234L43.4881 25.4124L43.3685 23.6131C42.5807 14.2044 34.9806 6.71261 25.6214 6.15397L25.5726 6.15106L24.6112 6.03055ZM22.2168 8.83893C30.2803 8.17709 37.8013 13.3446 40.0438 21.2811C40.269 22.0783 39.8054 22.9072 39.0082 23.1324C38.2109 23.3577 37.3821 22.894 37.1568 22.0968C35.3037 15.5383 29.093 11.2839 22.4606 11.829L22.4568 11.8293C15.6092 12.3743 10.1514 17.8288 9.49593 24.7299L9.49164 24.775L9.48517 24.8164L9.4844 24.8216L9.47937 24.8579C9.47461 24.8934 9.46728 24.9513 9.45861 25.0301C9.44126 25.1879 9.41858 25.4296 9.4004 25.7446C9.36402 26.3752 9.3459 27.2966 9.42381 28.4258C9.57985 30.6875 10.1192 33.7576 11.6477 36.9934C12.0016 37.7425 11.6812 38.6365 10.9321 38.9904C10.1831 39.3442 9.289 39.0238 8.93516 38.2748C7.21116 34.6251 6.60615 31.1722 6.43092 28.6323C6.34319 27.3607 6.36271 26.3115 6.40538 25.5718C6.42674 25.2017 6.45393 24.9083 6.47658 24.7023C6.48791 24.5993 6.4981 24.518 6.50588 24.4599L6.5129 24.4094C7.31887 16.108 13.8993 9.50183 22.2168 8.83893Z" fill="#EAE7E4"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.8543 24.1542C24.2626 23.5596 25.9956 24.3584 26.4884 25.9571ZM26.4884 25.9571C26.5232 26.0913 26.5603 26.2364 26.6 26.3919C27.3886 29.4774 29.218 36.6354 35.0609 43.3315C35.6056 43.9557 36.5531 44.0201 37.1773 43.4755C37.8015 42.9308 37.866 41.9833 37.3213 41.3591C31.9564 35.2107 30.2746 28.6461 29.4994 25.6202C29.4586 25.4612 29.4204 25.3119 29.3843 25.173L29.3774 25.1468L29.3697 25.1207C28.4071 21.9033 24.8498 20.0553 21.6874 21.3905C18.9068 22.5646 18.0009 25.068 18.0002 26.9985C17.9967 28.7383 18.4925 30.7277 19.1614 32.6486C19.839 34.5945 20.7355 36.5845 21.6192 38.3595C22.5049 40.1382 23.3892 41.7235 24.0514 42.8635C24.3829 43.4341 24.6598 43.8948 24.8546 44.2142C24.952 44.3739 25.029 44.4984 25.0821 44.5837L25.1435 44.6819L25.16 44.7081L25.1664 44.7182C25.1664 44.7182 25.1666 44.7186 26.4336 43.9156L25.1664 44.7182C25.6098 45.4179 26.5368 45.6261 27.2366 45.1826C27.9362 44.7392 28.144 43.8126 27.7007 43.1128L27.6971 43.1072L27.6838 43.086L27.6291 42.9984C27.5804 42.9203 27.5082 42.8035 27.4157 42.6519C27.2307 42.3486 26.9649 41.9064 26.6454 41.3565C26.0056 40.2552 25.1541 38.7282 24.3048 37.0223C23.4535 35.3128 22.6158 33.4461 21.9946 31.6621C21.3649 29.8538 20.9975 28.2406 21.0002 27.0035L21.0002 27.0004C21.0003 25.9312 21.47 24.7388 22.8543 24.1542" fill="#EAE7E4"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -302,6 +302,32 @@ SPStreamEvent newLoginTfaRequestedEvent(QString const &username) {
}
//****************************************************************************************************************************************************
/// \param[in] username The username.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newLoginFidoRequestedEvent(QString const &username) {
auto event = new ::grpc::LoginFidoRequestedEvent;
event->set_username(username.toStdString());
auto loginEvent = new grpc::LoginEvent;
loginEvent->set_allocated_fidorequested(event);
return wrapLoginEvent(loginEvent);
}
//****************************************************************************************************************************************************
/// \param[in] username The username.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newLoginTfaOrFidoRequestedEvent(QString const &username) {
auto event = new ::grpc::LoginTfaOrFidoRequestedEvent;
event->set_username(username.toStdString());
auto loginEvent = new grpc::LoginEvent;
loginEvent->set_allocated_tfaorfidorequested(event);
return wrapLoginEvent(loginEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************

View File

@ -48,7 +48,9 @@ SPStreamEvent newLoginTfaRequestedEvent(QString const &username); ///< Create a
SPStreamEvent newLoginTwoPasswordsRequestedEvent(QString const &username); ///< Create a new LoginTwoPasswordsRequestedEvent event.
SPStreamEvent newLoginFinishedEvent(QString const &userID, bool wasSignedOut); ///< Create a new LoginFinishedEvent event.
SPStreamEvent newLoginAlreadyLoggedInEvent(QString const &userID); ///< Create a new LoginAlreadyLoggedInEvent event.
SPStreamEvent newLoginHvRequestedEvent(); ///< Create a new LoginHvRequestedEvent
SPStreamEvent newLoginHvRequestedEvent(); ///< Create a new LoginHvRequestedEvent.
SPStreamEvent newLoginFidoRequestedEvent(QString const &username); ///< Create a new LoginFidoRequestedEvent.
SPStreamEvent newLoginTfaOrFidoRequestedEvent(QString const &username); ///< Create a new LoginTfaOrFidoRequestedEvent.
// Update related events
SPStreamEvent newUpdateErrorEvent(grpc::UpdateErrorType errorType); ///< Create a new UpdateErrorEvent event.

View File

@ -632,6 +632,28 @@ grpc::Status GRPCClient::login2FA(QString const &username, QString const &code)
return this->logGRPCCallStatus(stub_->Login2FA(this->clientContext().get(), request, &empty), __FUNCTION__);
}
//****************************************************************************************************************************************************
/// \param[in] username The username.
/// \param[in] code The Security key PIN.
/// \return the status for the gRPC call.
//****************************************************************************************************************************************************
grpc::Status GRPCClient::loginFido(const QString &username, const QString &pin) {
LoginRequest request;
request.set_username(username.toStdString());
request.set_password(pin.toStdString());
return this->logGRPCCallStatus(stub_->LoginFido(this->clientContext().get(), request, &empty), __FUNCTION__ );
}
//****************************************************************************************************************************************************
/// \param[in] username The username.
/// \return the status for the gRPC call.
//****************************************************************************************************************************************************
grpc::Status GRPCClient::abortFidoAssertion(const QString &username) {
LoginAbortRequest request;
request.set_username(username.toStdString());
return this->logGRPCCallStatus(stub_->FidoAssertionAbort(this->clientContext().get(), request, &empty), __FUNCTION__);
}
//****************************************************************************************************************************************************
/// \param[in] username The username.
@ -1256,6 +1278,15 @@ void GRPCClient::processLoginEvent(LoginEvent const &event) {
case HV_ERROR:
emit loginHvError(QString::fromStdString(error.message()));
break;
case FIDO_PIN_INVALID:
emit loginFidoPinInvalid(QString::fromStdString(error.message()));
break;
case FIDO_PIN_BLOCKED:
emit loginFidoPinBlocked(QString::fromStdString(error.message()));
break;
case FIDO_ERROR:
emit loginFidoError(QString::fromStdString(error.message()));
break;
default:
this->logError("Unknown login error event received.");
break;
@ -1266,6 +1297,14 @@ void GRPCClient::processLoginEvent(LoginEvent const &event) {
this->logTrace("Login event received: TfaRequested.");
emit login2FARequested(QString::fromStdString(event.tfarequested().username()));
break;
case LoginEvent::kFidoRequested:
this->logTrace("Login event received: FidoRequested.");
emit loginFidoRequested(QString::fromStdString(event.fidorequested().username()));
break;
case LoginEvent::kTfaOrFidoRequested:
this->logTrace("Login event received: TfaOrFidoRequested.");
emit login2FAOrFidoRequested(QString::fromStdString(event.tfaorfidorequested().username()));
break;
case LoginEvent::kTwoPasswordRequested:
this->logTrace("Login event received: TwoPasswordRequested.");
emit login2PasswordRequested(QString::fromStdString(event.twopasswordrequested().username()));
@ -1284,6 +1323,18 @@ void GRPCClient::processLoginEvent(LoginEvent const &event) {
this->logTrace("Login event Received: HvRequested");
emit loginHvRequested(QString::fromStdString(event.hvrequested().hvurl()));
break;
case LoginEvent::kLoginFidoTouchRequested:
this->logTrace("Login event received: FidoTouchRequested");
emit loginFidoTouchRequested(QString::fromStdString(event.loginfidotouchrequested().username()));
break;
case LoginEvent::kLoginFidoTouchCompleted:
this->logTrace("Login event received: FidoTouchCompleted");
emit loginFidoTouchCompleted(QString::fromStdString(event.loginfidotouchcompleted().username()));
break;
case LoginEvent::kLoginFidoPinRequired:
this->logTrace("Login event received: FidoPinRequired");
emit loginFidoPinRequired(QString::fromStdString(event.loginfidopinrequired().username()));
break;
default:
this->logError("Unknown Login event received.");
break;

View File

@ -175,9 +175,11 @@ signals:
public: // login related calls
grpc::Status login(QString const &username, QString const &password); ///< Performs the 'login' call.
grpc::Status login2FA(QString const &username, QString const &code); ///< Performs the 'login2FA' call.
grpc::Status loginFido(QString const &username, QString const &pin); ///< Performs the 'loginFido' call.
grpc::Status login2Passwords(QString const &username, QString const &password); ///< Performs the 'login2Passwords' call.
grpc::Status loginAbort(QString const &username); ///< Performs the 'loginAbort' call.
grpc::Status loginHv(QString const &username, QString const &password); ///< Performs the 'login' call with additional useHv flag
grpc::Status loginHv(QString const &username, QString const &password); ///< Performs the 'login' call with additional useHv flag.
grpc::Status abortFidoAssertion(const QString &username); ///< Performs the 'abortFidoAssertion' call.
signals:
void loginUsernamePasswordError(QString const &errMsg);
@ -186,6 +188,8 @@ signals:
void login2FARequested(QString const &username);
void login2FAError(QString const &errMsg);
void login2FAErrorAbort(QString const &errMsg);
void loginFidoRequested(QString const &username);
void login2FAOrFidoRequested(QString const &username);
void login2PasswordRequested(QString const &username);
void login2PasswordError(QString const &errMsg);
void login2PasswordErrorAbort(QString const &errMsg);
@ -193,6 +197,12 @@ signals:
void loginAlreadyLoggedIn(QString const &userID);
void loginHvRequested(QString const &hvUrl);
void loginHvError(QString const &errMsg);
void loginFidoTouchRequested(QString const &username);
void loginFidoTouchCompleted(QString const &username);
void loginFidoPinRequired(QString const &username);
void loginFidoPinInvalid(QString const &errMsg);
void loginFidoPinBlocked(QString const &errMsg);
void loginFidoError(QString const &errMsg);
public: // Update related calls
grpc::Status checkUpdate();