From eefc7e5f3e490e2755b149e943bad51e6df44bbe Mon Sep 17 00:00:00 2001
From: Nicolas Werner <nicolas.werner@hotmail.de>
Date: Sat, 9 May 2020 15:58:14 +0200
Subject: [PATCH] Add SSO endpoints

---
 include/mtx/responses/login.hpp   | 17 +++++++++++++++++
 include/mtx/user_interactive.hpp  |  4 ++++
 include/mtxclient/http/client.hpp |  7 +++++++
 lib/http/client.cpp               | 19 +++++++++++++++++++
 lib/structs/responses/login.cpp   | 11 +++++++++++
 lib/structs/user_interactive.cpp  |  1 +
 tests/client_api.cpp              | 13 +++++++++++++
 7 files changed, 72 insertions(+)

diff --git a/include/mtx/responses/login.hpp b/include/mtx/responses/login.hpp
index b218f7b55..e38f30e9e 100644
--- a/include/mtx/responses/login.hpp
+++ b/include/mtx/responses/login.hpp
@@ -10,6 +10,7 @@
 #endif
 
 #include "mtx/identifiers.hpp"
+#include "mtx/user_interactive.hpp"
 #include "well-known.hpp"
 
 namespace mtx {
@@ -35,5 +36,21 @@ struct Login
 
 void
 from_json(const nlohmann::json &obj, Login &response);
+
+struct LoginFlow
+{
+        mtx::user_interactive::AuthType type;
+};
+void
+from_json(const nlohmann::json &obj, LoginFlow &response);
+
+//! Response from the `GET /_matrix/client/r0/login` endpoint.
+struct LoginFlows
+{
+        std::vector<LoginFlow> flows;
+};
+
+void
+from_json(const nlohmann::json &obj, LoginFlows &response);
 }
 }
diff --git a/include/mtx/user_interactive.hpp b/include/mtx/user_interactive.hpp
index 7245a159b..fde1360d0 100644
--- a/include/mtx/user_interactive.hpp
+++ b/include/mtx/user_interactive.hpp
@@ -18,6 +18,7 @@ constexpr std::string_view oauth2         = "m.login.oauth2";
 constexpr std::string_view email_identity = "m.login.email.identity";
 constexpr std::string_view msisdn         = "m.login.msisdn";
 constexpr std::string_view token          = "m.login.token";
+constexpr std::string_view sso            = "m.login.sso"; // needed for /login at least
 constexpr std::string_view dummy          = "m.login.dummy";
 constexpr std::string_view terms          = "m.login.terms"; // see MSC1692
 }
@@ -141,6 +142,8 @@ struct OAuth2
 {};
 struct Terms
 {};
+struct SSO
+{};
 struct Dummy
 {};
 struct Fallback
@@ -159,6 +162,7 @@ struct Auth
                      auth::MSISDN,
                      auth::OAuth2,
                      auth::Terms,
+                     auth::SSO,
                      auth::Dummy,
                      auth::Fallback>
           content;
diff --git a/include/mtxclient/http/client.hpp b/include/mtxclient/http/client.hpp
index 7df7b7873..5f02ee091 100644
--- a/include/mtxclient/http/client.hpp
+++ b/include/mtxclient/http/client.hpp
@@ -51,6 +51,7 @@ struct GroupProfile;
 struct JoinedGroups;
 struct KeyChanges;
 struct Login;
+struct LoginFlows;
 struct Messages;
 struct Notifications;
 struct Profile;
@@ -193,6 +194,12 @@ public:
                    const std::string &device_name,
                    Callback<mtx::responses::Login> cb);
         void login(const mtx::requests::Login &req, Callback<mtx::responses::Login> cb);
+
+        //! Get the supported login flows
+        void get_login(Callback<mtx::responses::LoginFlows> cb);
+        //! Get url to navigate to for sso login flow
+        //! Open this in a browser
+        std::string login_sso_redirect(std::string redirectUrl);
         //! Lookup real server to connect to.
         //! Call set_server with the returned homeserver url after this
         void well_known(Callback<mtx::responses::WellKnown> cb);
diff --git a/lib/http/client.cpp b/lib/http/client.cpp
index 79547914e..9624e9282 100644
--- a/lib/http/client.cpp
+++ b/lib/http/client.cpp
@@ -291,6 +291,25 @@ Client::login(const mtx::requests::Login &req, Callback<mtx::responses::Login> c
           false);
 }
 
+void
+Client::get_login(Callback<mtx::responses::LoginFlows> cb)
+{
+        get<mtx::responses::LoginFlows>(
+          "/client/r0/login",
+          [cb](const mtx::responses::LoginFlows &res, HeaderFields, RequestErr err) {
+                  cb(res, err);
+          },
+          false);
+}
+
+std::string
+Client::login_sso_redirect(std::string redirectUrl)
+{
+        return "https://" + server() + ":" + std::to_string(port()) +
+               "/_matrix/client/r0/login/sso/redirect?" +
+               mtx::client::utils::query_params({{"redirectUrl", redirectUrl}});
+}
+
 void
 Client::well_known(Callback<mtx::responses::WellKnown> callback)
 {
diff --git a/lib/structs/responses/login.cpp b/lib/structs/responses/login.cpp
index 894fd261d..5ed8bfa8c 100644
--- a/lib/structs/responses/login.cpp
+++ b/lib/structs/responses/login.cpp
@@ -19,5 +19,16 @@ from_json(const nlohmann::json &obj, Login &response)
         if (obj.count("well_known") != 0)
                 response.well_known = obj.at("well_known").get<WellKnown>();
 }
+
+void
+from_json(const nlohmann::json &obj, LoginFlow &response)
+{
+        response.type = obj.at("type").get<std::string>();
+}
+void
+from_json(const nlohmann::json &obj, LoginFlows &response)
+{
+        response.flows = obj.at("flows").get<std::vector<LoginFlow>>();
+}
 }
 }
diff --git a/lib/structs/user_interactive.cpp b/lib/structs/user_interactive.cpp
index 7e148ba57..703261a37 100644
--- a/lib/structs/user_interactive.cpp
+++ b/lib/structs/user_interactive.cpp
@@ -116,6 +116,7 @@ to_json(nlohmann::json &obj, const Auth &auth)
                              obj["threepidCreds"] = id.threepidCreds;
                      },
                      [&obj](const auth::OAuth2 &) { obj["type"] = auth_types::oauth2; },
+                     [&obj](const auth::SSO &) { obj["type"] = auth_types::sso; },
                      [&obj](const auth::Terms &) { obj["type"] = auth_types::terms; },
                      [&obj](const auth::Dummy &) { obj["type"] = auth_types::dummy; },
                      [](const auth::Fallback &) {},
diff --git a/tests/client_api.cpp b/tests/client_api.cpp
index 8034a4bf5..c56a837c9 100644
--- a/tests/client_api.cpp
+++ b/tests/client_api.cpp
@@ -113,6 +113,19 @@ TEST(ClientAPI, LoginWrongUsername)
         mtx_client->close();
 }
 
+TEST(ClientAPI, LoginFlows)
+{
+        std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
+
+        mtx_client->get_login([](const mtx::responses::LoginFlows &res, RequestErr err) {
+                ASSERT_FALSE(err);
+
+                EXPECT_EQ(res.flows[0].type, mtx::user_interactive::auth_types::password);
+        });
+
+        mtx_client->close();
+}
+
 TEST(ClientAPI, EmptyUserAvatar)
 {
         auto alice = std::make_shared<Client>("localhost");
-- 
GitLab