Compare commits

...

1 Commits

Author SHA1 Message Date
Tobias Fella
5edb3e5145 Implement OIDC login 2024-08-09 14:21:46 +02:00
7 changed files with 145 additions and 1 deletions

View File

@@ -9,12 +9,18 @@
#include <KLocalizedString>
#include <QGuiApplication>
#include <QNetworkReply>
#include <QRandomGenerator>
#include <QTcpServer>
#include <QTimer>
#include <QCoroNetworkReply>
#include <signal.h>
#include <Quotient/accountregistry.h>
#include <Quotient/csapi/notifications.h>
#include <Quotient/csapi/wellknown.h>
#include <Quotient/qt_connection_util.h>
#include <Quotient/settings.h>
@@ -45,6 +51,7 @@
bool testMode = false;
using namespace Quotient;
using namespace Qt::Literals::StringLiterals;
Controller::Controller(QObject *parent)
: QObject(parent)
@@ -210,7 +217,12 @@ void Controller::invokeLogin()
connect(connection, &NeoChatConnection::networkError, this, [this](const QString &error, const QString &, int, int) {
Q_EMIT errorOccured(i18n("Network Error: %1", error), {});
});
connection->assumeIdentity(account.userId(), accessToken);
if (!account.clientId().isEmpty()) {
connection->assumeOidcIdentity(account.userId(), accessToken, account.clientId(), account.tokenEndpoint());
} else {
connection->assumeIdentity(account.userId(), accessToken);
}
});
}
}
@@ -416,4 +428,22 @@ bool Controller::csSupported() const
#endif
}
NeoChatConnection *Controller::pendingOidcConnection() const
{
return m_pendingOidcConnection.get();
}
void Controller::startOidcLogin(const QString &homeserver)
{
auto url = QUrl::fromUserInput(homeserver.startsWith(u"https:"_s) ? homeserver : u"https://%1"_s.arg(homeserver));
m_pendingOidcConnection = new NeoChatConnection(url, this);
Q_EMIT pendingOidcConnectionChanged();
m_pendingOidcConnection->startOidcLogin();
connect(m_pendingOidcConnection, &Connection::connected, this, [this]() {
m_pendingOidcConnection->loadState();
addConnection(m_pendingOidcConnection);
setActiveConnection(m_pendingOidcConnection);
});
}
#include "moc_controller.cpp"

View File

@@ -52,6 +52,8 @@ class Controller : public QObject
Q_PROPERTY(bool csSupported READ csSupported CONSTANT)
Q_PROPERTY(NeoChatConnection *pendingOidcConnection READ pendingOidcConnection NOTIFY pendingOidcConnectionChanged)
public:
/**
* @brief Define the types on inline messages that can be shown.
@@ -106,8 +108,12 @@ public:
Q_INVOKABLE void removeConnection(const QString &userId);
Q_INVOKABLE void startOidcLogin(const QString &homeserver);
bool csSupported() const;
NeoChatConnection *pendingOidcConnection() const;
private:
explicit Controller(QObject *parent = nullptr);
@@ -119,10 +125,13 @@ private:
void loadSettings();
void saveSettings() const;
QCoro::Task<void> doStartOidcLogin(QString homeserver);
Quotient::AccountRegistry m_accountRegistry;
QStringList m_accountsLoading;
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
QString m_endpoint;
QPointer<NeoChatConnection> m_pendingOidcConnection;
private Q_SLOTS:
void invokeLogin();
@@ -136,4 +145,5 @@ Q_SIGNALS:
void activeConnectionChanged(NeoChatConnection *connection);
void accountsLoadingChanged();
void showMessage(MessageType messageType, const QString &message);
void pendingOidcConnectionChanged();
};

View File

@@ -20,4 +20,5 @@ ecm_add_qml_module(login GENERATE_PLUGIN_SOURCE
Sso.qml
Terms.qml
Username.qml
Oidc.qml
)

View File

@@ -31,4 +31,11 @@ LoginStep {
text: i18nc("@action:button", "Register")
onClicked: root.processed("Homeserver")
}
FormCard.FormDelegateSeparator {}
FormCard.FormButtonDelegate {
text: i18nc("@action:button", "Login with OIDC")
onClicked: root.processed("Oidc")
}
}

74
src/login/Oidc.qml Normal file
View File

@@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick
import QtQuick.Layouts
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.neochat
LoginStep {
id: root
FormCard.FormTextFieldDelegate {
id: homeserver
visible: !infoLabel.visible
label: i18n("Homeserver")
text: "synapse-oidc.element.dev"
}
Connections {
target: Controller.pendingOidcConnection
function onOpenOidcUrl(url: string): void {
infoLabel.url = url;
infoLabel.visible = true;
}
function onConnected(): void {
root.processed("Loading");
}
}
FormCard.FormTextDelegate {
id: infoLabel
property string url: ""
visible: false
text: i18n("To continue, please open the following link in your web browser: %1", "<br /><br /><a href='" + url + "'>" + url + "</a>")
onLinkActivated: url => Qt.openUrlExternally(url)
}
FormCard.FormDelegateSeparator { above: openLink }
FormCard.FormButtonDelegate {
id: openLink
visible: infoLabel.visible
text: i18n("Open Authorization Link")
icon.name: "document-open"
onClicked: Qt.openUrlExternally(infoLabel.url.authorizeUrl)
}
FormCard.FormDelegateSeparator {
visible: infoLabel.visible
above: openLink
below: copyLink
}
FormCard.FormButtonDelegate {
id: copyLink
visible: infoLabel.visible
text: i18n("Copy Authorization Link")
icon.name: "edit-copy"
onClicked: {
Clipboard.saveText(infoLabel.url)
applicationWindow().showPassiveNotification(i18n("Link copied."));
}
}
FormCard.FormButtonDelegate {
visible: !infoLabel.visible
text: i18nc("@action:button", "Log in with OIDC")
onClicked: Controller.startOidcLogin(homeserver.text)
}
}

View File

@@ -23,6 +23,7 @@
#include <KLocalizedString>
#include <Quotient/connectiondata.h>
#include <Quotient/csapi/content-repo.h>
#include <Quotient/csapi/profile.h>
#include <Quotient/csapi/versions.h>
@@ -148,6 +149,17 @@ void NeoChatConnection::connectSignals()
});
},
Qt::SingleShotConnection);
connect(this, &Connection::refreshTokenChanged, this, [this]() {
QKeychain::WritePasswordJob job(qAppName());
job.setAutoDelete(true);
job.setKey(userId());
job.setBinaryData(connectionData()->refreshToken().toLatin1());
QEventLoop loop;
connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.start();
loop.exec();
});
}
int NeoChatConnection::badgeNotificationCount() const

View File

@@ -54,6 +54,16 @@ Kirigami.ApplicationWindow {
}
}
Connections {
id: pendingOidcConnections
target: Controller.pendingOidcConnection
function onConnected() {
console.warn("loading rooms")
root.load();
pendingOidcConnections.enabled = false;
}
}
Connections {
target: root.quitAction
function onTriggered() {