Add support for SSO authentication

This commit is contained in:
miruka
2020-07-25 23:31:13 -04:00
parent d7907db547
commit 157ea2ffb2
11 changed files with 405 additions and 147 deletions

View File

@@ -7,8 +7,12 @@ import "../../Base"
HSwipeView {
id: swipeView
clip: true
interactive: currentIndex !== 0 || signIn.serverUrl
onCurrentItemChanged: if (currentIndex === 0) serverBrowser.takeFocus()
interactive: serverBrowser.acceptedUrl
onCurrentItemChanged:
currentIndex === 0 ?
serverBrowser.takeFocus() :
signInLoader.takeFocus()
Component.onCompleted: serverBrowser.takeFocus()
HPage {
@@ -26,6 +30,8 @@ HSwipeView {
HPage {
id: tabPage
enabled: swipeView.currentItem === this
HTabbedBox {
anchors.centerIn: parent
width: Math.min(implicitWidth, tabPage.availableWidth)
@@ -37,11 +43,32 @@ HSwipeView {
HTabButton { text: qsTr("Reset") }
}
SignIn {
id: signIn
serverUrl: serverBrowser.acceptedUrl
displayUrl: serverBrowser.acceptedUserUrl
onExitRequested: swipeView.currentIndex = 0
HLoader {
id: signInLoader
readonly property Component signInPassword: SignInPassword {
serverUrl: serverBrowser.acceptedUrl
displayUrl: serverBrowser.acceptedUserUrl
onExitRequested: swipeView.currentIndex = 0
}
readonly property Component signInSso: SignInSso {
serverUrl: serverBrowser.acceptedUrl
displayUrl: serverBrowser.acceptedUserUrl
onExitRequested: swipeView.currentIndex = 0
}
function takeFocus() { if (item) item.takeFocus() }
sourceComponent:
serverBrowser.loginFlows.includes("m.login.password") ?
signInPassword :
serverBrowser.loginFlows.includes("m.login.sso") &&
serverBrowser.loginFlows.includes("m.login.token") ?
signInSso :
null
}
Register {}

View File

@@ -11,7 +11,7 @@ HBox {
property string acceptedUserUrl: ""
property string acceptedUrl: ""
property var loginFlows: ["m.login.password"]
property var loginFlows: []
property string saveName: "serverBrowser"
property var saveProperties: ["acceptedUserUrl"]
@@ -30,8 +30,20 @@ HBox {
connectFuture = py.callCoro("server_info", args, ([url, flows]) => {
connectTimeout.stop()
errorMessage.text = ""
connectFuture = null
if (! (
flows.includes("m.login.password") ||
(
flows.includes("m.login.sso") &&
flows.includes("m.login.token")
)
)) {
errorMessage.text =
qsTr("No supported sign-in method for this homeserver.")
return
}
connectFuture = null
acceptedUrl = url
acceptedUserUrl = args[0]
loginFlows = flows

View File

@@ -12,66 +12,42 @@ HFlickableColumnPage {
property string serverUrl
property string displayUrl: serverUrl
property var loginFuture: null
signal exitRequested()
property var loginFuture: null
readonly property int security:
serverUrl.startsWith("https://") ?
SignIn.Security.Secure :
SignInBase.Security.Secure :
["//localhost", "//127.0.0.1", "//:1"].includes(
serverUrl.split(":")[1],
) ?
SignIn.Security.LocalHttp :
SignInBase.Security.LocalHttp :
SignIn.Security.Insecure
SignInBase.Security.Insecure
function takeFocus() { idField.item.forceActiveFocus() }
default property alias innerData: inner.data
readonly property alias rememberAccount: rememberAccount
readonly property alias errorMessage: errorMessage
readonly property alias applyButton: applyButton
function signIn() {
if (page.loginFuture) page.loginFuture.cancel()
signal exitRequested()
function finishSignIn(receivedUserId) {
errorMessage.text = ""
page.loginFuture = null
const args = [
idField.item.text.trim(), passwordField.item.text,
undefined, page.serverUrl,
]
py.callCoro(
rememberAccount.checked ?
"saved_accounts.add":
"saved_accounts.delete",
page.loginFuture = py.callCoro("login_client", args, userId => {
errorMessage.text = ""
page.loginFuture = null
[receivedUserId]
)
print(rememberAccount.checked)
py.callCoro(
rememberAccount.checked ?
"saved_accounts.add": "saved_accounts.delete",
[userId]
)
pageLoader.showPage(
"AccountSettings/AccountSettings", {userId}
)
}, (type, args, error, traceback, uuid) => {
page.loginFuture = null
let txt = qsTr(
"Invalid request, login type or unknown error: %1",
).arg(type)
type === "MatrixForbidden" ?
txt = qsTr("Invalid username or password") :
type === "MatrixUserDeactivated" ?
txt = qsTr("This account was deactivated") :
utils.showError(type, traceback, uuid)
errorMessage.text = txt
})
pageLoader.showPage(
"AccountSettings/AccountSettings", {userId: receivedUserId}
)
}
function cancel() {
@@ -91,12 +67,11 @@ HFlickableColumnPage {
footer: AutoDirectionLayout {
ApplyButton {
id: applyButton
enabled: idField.item.text.trim() && passwordField.item.text
text: qsTr("Sign in")
icon.name: "sign-in"
loading: page.loginFuture !== null
disableWhileLoading: false
onClicked: page.signIn()
}
CancelButton {
@@ -104,27 +79,28 @@ HFlickableColumnPage {
}
}
onKeyboardAccept: if (applyButton.enabled) page.signIn()
onKeyboardAccept: if (applyButton.enabled) applyButton.clicked()
onKeyboardCancel: page.cancel()
Component.onDestruction: if (loginFuture) loginFuture.cancel()
HButton {
icon.name: "sign-in-" + (
page.security === SignIn.Security.Insecure ? "insecure" :
page.security === SignIn.Security.LocalHttp ? "local-http" :
page.security === SignInBase.Security.Insecure ? "insecure" :
page.security === SignInBase.Security.LocalHttp ? "local-http" :
"secure"
)
icon.color:
page.security === SignIn.Security.Insecure ?
page.security === SignInBase.Security.Insecure ?
theme.colors.negativeBackground :
page.security === SignIn.Security.LocalHttp ?
page.security === SignInBase.Security.LocalHttp ?
theme.colors.middleBackground :
theme.colors.positiveBackground
text:
page.security === SignIn.Security.Insecure ?
page.security === SignInBase.Security.Insecure ?
page.serverUrl :
page.displayUrl.replace(/^(https?:\/\/)?(www\.)?/, "")
@@ -134,27 +110,9 @@ HFlickableColumnPage {
Layout.maximumWidth: parent.width
}
HLabeledItem {
id: idField
label.text: qsTr("Username:")
Layout.fillWidth: true
HTextField {
width: parent.width
}
}
HLabeledItem {
id: passwordField
label.text: qsTr("Password:")
Layout.fillWidth: true
HTextField {
width: parent.width
echoMode: HTextField.Password
}
HColumnLayout {
id: inner
spacing: page.column.spacing
}
HCheckBox {

View File

@@ -0,0 +1,69 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
import "../../Base/Buttons"
SignInBase {
id: page
function takeFocus() { idField.item.forceActiveFocus() }
function signIn() {
if (page.loginFuture) page.loginFuture.cancel()
errorMessage.text = ""
page.loginFuture = py.callCoro(
"password_auth",
[idField.item.text.trim(), passField.item.text, page.serverUrl],
page.finishSignIn,
(type, args, error, traceback, uuid) => {
page.loginFuture = null
let txt = qsTr(
"Invalid request, login type or unknown error: %1",
).arg(type)
type === "MatrixForbidden" ?
txt = qsTr("Invalid username or password") :
type === "MatrixUserDeactivated" ?
txt = qsTr("This account was deactivated") :
utils.showError(type, traceback, uuid)
page.errorMessage.text = txt
},
)
}
applyButton.enabled: idField.item.text.trim() && passField.item.text
applyButton.onClicked: page.signIn()
HLabeledItem {
id: idField
label.text: qsTr("Username:")
Layout.fillWidth: true
HTextField {
width: parent.width
}
}
HLabeledItem {
id: passField
label.text: qsTr("Password:")
Layout.fillWidth: true
HTextField {
width: parent.width
echoMode: HTextField.Password
}
}
}

View File

@@ -0,0 +1,55 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
import "../../Base/Buttons"
SignInBase {
id: page
function takeFocus() { urlField.forceActiveFocus() }
function startSignIn() {
errorMessage.text = ""
page.loginFuture = py.callCoro("start_sso_auth", [serverUrl], url => {
urlField.text = url
urlField.cursorPosition = 0
Qt.openUrlExternally(url)
page.loginFuture = py.callCoro("continue_sso_auth", [], userId => {
page.loginFuture = null
page.finishSignIn(userId)
})
})
}
applyButton.text: qsTr("Waiting")
applyButton.loading: true
Component.onCompleted: page.startSignIn()
HLabel {
wrapMode: HLabel.Wrap
text: qsTr(
"Complete the single sign-on process in your web browser to " +
"continue.\n\n" +
"If no page appeared, you can also manually open this address:"
)
Layout.fillWidth: true
}
HTextArea {
id: urlField
width: parent.width
readOnly: true
radius: 0
wrapMode: HTextArea.WrapAnywhere
Layout.fillWidth: true
Layout.fillHeight: true
}
}

View File

@@ -86,6 +86,7 @@ HMxcImage {
gesturePolicy: TapHandler.ReleaseWithinBounds
onTapped: {
print(loader.mediaUrl, loader.singleMediaInfo.media_http_url)
if (eventList.selectedCount) {
eventDelegate.toggleChecked()
return

View File

@@ -10,6 +10,7 @@ import "MainPane"
Item {
id: mainUI
enabled: ! window.anyPopup
property bool accountsPresent:
ModelStore.get("accounts").count > 0 || py.startupAnyAccountsSaved