summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2020-05-09 23:31:00 +0200
committerNicolas Werner <nicolas.werner@hotmail.de>2020-05-09 23:33:03 +0200
commit7b1fa60cc6c74a53de0af636fa8f4f06caf87fa0 (patch)
tree30a2b6247c7b7be8c03995d0474f87df7019bb29 /src
parentImprove Login and Register page hinting (diff)
downloadnheko-7b1fa60cc6c74a53de0af636fa8f4f06caf87fa0.tar.xz
Add SSO
closes #94
Diffstat (limited to 'src')
-rw-r--r--src/ChatPage.cpp8
-rw-r--r--src/LoginPage.cpp113
-rw-r--r--src/LoginPage.h11
-rw-r--r--src/SSOHandler.cpp54
-rw-r--r--src/SSOHandler.h24
5 files changed, 183 insertions, 27 deletions
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp

index ae3c7a11..7c4aac77 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp
@@ -988,8 +988,12 @@ ChatPage::trySync() const auto err_code = mtx::errors::to_string(err->matrix_error.errcode); const int status_code = static_cast<int>(err->status_code); - if (http::is_logged_in() && err->matrix_error.errcode == - mtx::errors::ErrorCode::M_UNKNOWN_TOKEN) { + if ((http::is_logged_in() && + (err->matrix_error.errcode == + mtx::errors::ErrorCode::M_UNKNOWN_TOKEN || + err->matrix_error.errcode == + mtx::errors::ErrorCode::M_MISSING_TOKEN)) || + !http::is_logged_in()) { emit dropToLoginPageCb(msg); return; } diff --git a/src/LoginPage.cpp b/src/LoginPage.cpp
index 6d96419a..4c3999ec 100644 --- a/src/LoginPage.cpp +++ b/src/LoginPage.cpp
@@ -15,28 +15,35 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QDesktopServices> #include <QPainter> #include <QStyleOption> #include <mtx/identifiers.hpp> +#include <mtx/requests.hpp> #include <mtx/responses/login.hpp> #include "Config.h" #include "Logging.h" #include "LoginPage.h" #include "MatrixClient.h" +#include "SSOHandler.h" #include "ui/FlatButton.h" #include "ui/LoadingIndicator.h" #include "ui/OverlayModal.h" #include "ui/RaisedButton.h" #include "ui/TextField.h" +Q_DECLARE_METATYPE(LoginPage::LoginMethod) + using namespace mtx::identifiers; LoginPage::LoginPage(QWidget *parent) : QWidget(parent) , inferredServerAddress_() { + qRegisterMetaType<LoginPage::LoginMethod>("LoginPage::LoginMethod"); + top_layout_ = new QVBoxLayout(); top_bar_layout_ = new QHBoxLayout(); @@ -226,7 +233,8 @@ LoginPage::onMatrixIdEntered() emit versionErrorCb(tr("Autodiscovery failed. Unknown error when " "requesting .well-known.")); nhlog::net()->error("Autodiscovery failed. Unknown error when " - "requesting .well-known."); + "requesting .well-known. {}", + err->status_code); return; } @@ -263,7 +271,16 @@ LoginPage::checkHomeserverVersion() return; } - emit versionOkCb(); + http::client()->get_login( + [this](mtx::responses::LoginFlows flows, mtx::http::RequestErr err) { + if (err || flows.flows.empty()) + emit versionOkCb(LoginMethod::Password); + + if (flows.flows[0].type == mtx::user_interactive::auth_types::sso) + emit versionOkCb(LoginMethod::SSO); + else + emit versionOkCb(LoginMethod::Password); + }); }); } @@ -294,12 +311,22 @@ LoginPage::versionError(const QString &error) } void -LoginPage::versionOk() +LoginPage::versionOk(LoginMethod loginMethod) { + this->loginMethod = loginMethod; + serverLayout_->removeWidget(spinner_); matrixidLayout_->removeWidget(spinner_); spinner_->stop(); + if (loginMethod == LoginMethod::SSO) { + password_input_->hide(); + login_button_->setText(tr("SSO LOGIN")); + } else { + password_input_->show(); + login_button_->setText(tr("LOGIN")); + } + if (serverInput_->isVisible()) serverInput_->hide(); } @@ -317,29 +344,68 @@ LoginPage::onLoginButtonClicked() return loginError("You have entered an invalid Matrix ID e.g @joe:matrix.org"); } - if (password_input_->text().isEmpty()) - return loginError(tr("Empty password")); + if (loginMethod == LoginMethod::Password) { + if (password_input_->text().isEmpty()) + return loginError(tr("Empty password")); - http::client()->login( - user.localpart(), - password_input_->text().toStdString(), - deviceName_->text().trimmed().isEmpty() ? initialDeviceName() - : deviceName_->text().toStdString(), - [this](const mtx::responses::Login &res, mtx::http::RequestErr err) { - if (err) { - emit loginError(QString::fromStdString(err->matrix_error.error)); - emit errorOccurred(); - return; - } + http::client()->login( + user.localpart(), + password_input_->text().toStdString(), + deviceName_->text().trimmed().isEmpty() ? initialDeviceName() + : deviceName_->text().toStdString(), + [this](const mtx::responses::Login &res, mtx::http::RequestErr err) { + if (err) { + emit loginError(QString::fromStdString(err->matrix_error.error)); + emit errorOccurred(); + return; + } - if (res.well_known) { - http::client()->set_server(res.well_known->homeserver.base_url); - nhlog::net()->info("Login requested to user server: " + - res.well_known->homeserver.base_url); - } + if (res.well_known) { + http::client()->set_server(res.well_known->homeserver.base_url); + nhlog::net()->info("Login requested to user server: " + + res.well_known->homeserver.base_url); + } - emit loginOk(res); - }); + emit loginOk(res); + }); + } else { + auto sso = new SSOHandler(); + connect(sso, &SSOHandler::ssoSuccess, this, [this, sso](std::string token) { + mtx::requests::Login req{}; + req.token = token; + req.type = mtx::user_interactive::auth_types::token; + req.device_id = deviceName_->text().trimmed().isEmpty() + ? initialDeviceName() + : deviceName_->text().toStdString(); + http::client()->login( + req, [this](const mtx::responses::Login &res, mtx::http::RequestErr err) { + if (err) { + emit loginError( + QString::fromStdString(err->matrix_error.error)); + emit errorOccurred(); + return; + } + + if (res.well_known) { + http::client()->set_server( + res.well_known->homeserver.base_url); + nhlog::net()->info("Login requested to user server: " + + res.well_known->homeserver.base_url); + } + + emit loginOk(res); + }); + sso->deleteLater(); + }); + connect(sso, &SSOHandler::ssoFailed, this, [this, sso]() { + emit loginError(tr("SSO login failed")); + emit errorOccurred(); + sso->deleteLater(); + }); + + QDesktopServices::openUrl( + QString::fromStdString(http::client()->login_sso_redirect(sso->url()))); + } emit loggingIn(); } @@ -349,6 +415,7 @@ LoginPage::reset() { matrixid_input_->clear(); password_input_->clear(); + password_input_->show(); serverInput_->clear(); spinner_->stop(); diff --git a/src/LoginPage.h b/src/LoginPage.h
index 4b84abfc..8a402aea 100644 --- a/src/LoginPage.h +++ b/src/LoginPage.h
@@ -38,6 +38,12 @@ class LoginPage : public QWidget Q_OBJECT public: + enum class LoginMethod + { + Password, + SSO, + }; + LoginPage(QWidget *parent = nullptr); void reset(); @@ -50,7 +56,7 @@ signals: //! Used to trigger the corresponding slot outside of the main thread. void versionErrorCb(const QString &err); void loginErrorCb(const QString &err); - void versionOkCb(); + void versionOkCb(LoginPage::LoginMethod method); void loginOk(const mtx::responses::Login &res); @@ -77,7 +83,7 @@ private slots: // Callback for errors produced during server probing void versionError(const QString &error_message); // Callback for successful server probing - void versionOk(); + void versionOk(LoginPage::LoginMethod method); private: bool isMatrixIdValid(); @@ -123,4 +129,5 @@ private: TextField *password_input_; TextField *deviceName_; TextField *serverInput_; + LoginMethod loginMethod = LoginMethod::Password; }; diff --git a/src/SSOHandler.cpp b/src/SSOHandler.cpp new file mode 100644
index 00000000..0ee2fc17 --- /dev/null +++ b/src/SSOHandler.cpp
@@ -0,0 +1,54 @@ +#include "SSOHandler.h" + +#include <QTimer> + +#include <thread> + +#include "Logging.h" + +SSOHandler::SSOHandler(QObject *) +{ + QTimer::singleShot(120000, this, &SSOHandler::ssoFailed); + + using namespace httplib; + + svr.set_logger([](const Request &req, const Response &res) { + nhlog::net()->info("req: {}, res: {}", req.path, res.status); + }); + + svr.Get("/sso", [this](const Request &req, Response &res) { + if (req.has_param("loginToken")) { + auto val = req.get_param_value("loginToken"); + res.set_content("SSO success", "text/plain"); + emit ssoSuccess(val); + } else { + res.set_content("Missing loginToken for SSO login!", "text/plain"); + emit ssoFailed(); + } + }); + + std::thread t([this]() { + this->port = svr.bind_to_any_port("localhost"); + svr.listen_after_bind(); + + }); + t.detach(); + + while (!svr.is_running()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +SSOHandler::~SSOHandler() +{ + svr.stop(); + while (svr.is_running()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +std::string +SSOHandler::url() const +{ + return "http://localhost:" + std::to_string(port) + "/sso"; +} diff --git a/src/SSOHandler.h b/src/SSOHandler.h new file mode 100644
index 00000000..325b7a58 --- /dev/null +++ b/src/SSOHandler.h
@@ -0,0 +1,24 @@ +#include "httplib.h" + +#include <QObject> +#include <string> + +class SSOHandler : public QObject +{ + Q_OBJECT + +public: + SSOHandler(QObject *parent = nullptr); + + ~SSOHandler(); + + std::string url() const; + +signals: + void ssoSuccess(std::string token); + void ssoFailed(); + +private: + httplib::Server svr; + int port = 0; +};