diff --git a/src/controller.cpp b/src/controller.cpp index e54f13e9b..5bf2aa419 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -9,12 +9,18 @@ #include #include +#include +#include +#include #include +#include + #include #include #include +#include #include #include @@ -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" diff --git a/src/controller.h b/src/controller.h index 4527082cd..9f47d7e10 100644 --- a/src/controller.h +++ b/src/controller.h @@ -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 doStartOidcLogin(QString homeserver); + Quotient::AccountRegistry m_accountRegistry; QStringList m_accountsLoading; QMap> m_connectionsLoading; QString m_endpoint; + QPointer 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(); }; diff --git a/src/login/CMakeLists.txt b/src/login/CMakeLists.txt index 9738e40a6..d44048d9a 100644 --- a/src/login/CMakeLists.txt +++ b/src/login/CMakeLists.txt @@ -20,4 +20,5 @@ ecm_add_qml_module(login GENERATE_PLUGIN_SOURCE Sso.qml Terms.qml Username.qml + Oidc.qml ) diff --git a/src/login/LoginRegister.qml b/src/login/LoginRegister.qml index e6767dba9..0fe1df0f9 100644 --- a/src/login/LoginRegister.qml +++ b/src/login/LoginRegister.qml @@ -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") + } } diff --git a/src/login/Oidc.qml b/src/login/Oidc.qml new file mode 100644 index 000000000..c67e02f86 --- /dev/null +++ b/src/login/Oidc.qml @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2024 Tobias Fella +// 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", "

" + url + "") + 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) + + } +} diff --git a/src/neochatconnection.cpp b/src/neochatconnection.cpp index 67c77b745..7fb5e63d2 100644 --- a/src/neochatconnection.cpp +++ b/src/neochatconnection.cpp @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -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 diff --git a/src/qml/Main.qml b/src/qml/Main.qml index 557d92ef8..5153e5436 100644 --- a/src/qml/Main.qml +++ b/src/qml/Main.qml @@ -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() {