Skip to content
Snippets Groups Projects
Verified Commit 7fe7a70f authored by Nicolas Werner's avatar Nicolas Werner
Browse files

Implement and fix 3pid UIA stages

parent cc0e9dff
No related branches found
No related tags found
No related merge requests found
Pipeline #1968 failed
...@@ -111,6 +111,74 @@ struct Login ...@@ -111,6 +111,74 @@ struct Login
void void
to_json(json &obj, const Login &request); to_json(json &obj, const Login &request);
/// @brief Request payload for
/// `POST /_matrix/client/r0/{register,account/password}/email/requestToken`
///
/// The homeserver should validate the email itself, either by sending a validation email itself or
/// by using a service it has control over.
struct RequestEmailToken
{
//! Required. A unique string generated by the client, and used to identify the validation
//! attempt. It must be a string consisting of the characters [0-9a-zA-Z.=_-]. Its length must
//! not exceed 255 characters and it must not be empty.
std::string client_secret;
//! Required. The email address to validate.
std::string email;
//! Required. The server will only send an email if the send_attempt is a number greater than
//! the most recent one which it has seen, scoped to that email + client_secret pair. This is to
//! avoid repeatedly sending the same email in the case of request retries between the POSTing
//! user and the identity server. The client should increment this value if they desire a new
//! email (e.g. a reminder) to be sent. If they do not, the server should respond with success
//! but not resend the email.
int send_attempt = 0;
};
void
to_json(json &obj, const RequestEmailToken &request);
/// @brief Request payload for
/// `POST /_matrix/client/r0/{register,account/password}/msisdn/requestToken`
///
/// The homeserver should validate the email itself, either by sending a validation email itself or
/// by using a service it has control over.
struct RequestMSISDNToken
{
//! Required. A unique string generated by the client, and used to identify the validation
//! attempt. It must be a string consisting of the characters [0-9a-zA-Z.=_-]. Its length must
//! not exceed 255 characters and it must not be empty.
std::string client_secret;
//! Required. The two-letter uppercase ISO-3166-1 alpha-2 country code that the number in
//! phone_number should be parsed as if it were dialled from.
std::string country;
//! Required. The phone number to validate.
std::string phone_number;
//! Required. The server will only send an SMS if the send_attempt is a number greater than the
//! most recent one which it has seen, scoped to that country + phone_number + client_secret
//! triple. This is to avoid repeatedly sending the same SMS in the case of request retries
//! between the POSTing user and the identity server. The client should increment this value if
//! they desire a new SMS (e.g. a reminder) to be sent.
int send_attempt = 0;
};
void
to_json(json &obj, const RequestMSISDNToken &request);
//! Validate ownership of an email address/phone number.
struct IdentitySubmitToken
{
//! Required. The session ID, generated by the requestToken call.
std::string sid;
//! Required. The client secret that was supplied to the requestToken call.
std::string client_secret;
//! Required. The token generated by the requestToken call and emailed to the user.
std::string token;
};
void
to_json(json &obj, const IdentitySubmitToken &request);
//! Request payload for the `PUT /_matrix/client/r0/sendToDevice/{eventType}/{transcationID}` //! Request payload for the `PUT /_matrix/client/r0/sendToDevice/{eventType}/{transcationID}`
template<typename EventContent> template<typename EventContent>
using ToDeviceMessages = std::map<mtx::identifiers::User, std::map<std::string, EventContent>>; using ToDeviceMessages = std::map<mtx::identifiers::User, std::map<std::string, EventContent>>;
......
...@@ -66,6 +66,34 @@ struct Version ...@@ -66,6 +66,34 @@ struct Version
void void
from_json(const nlohmann::json &obj, Version &response); from_json(const nlohmann::json &obj, Version &response);
//! Some endpoints return this to indicate success in addition to the http code.
struct Success
{
//! Required. Whether the validation was successful or not.
bool success;
};
void
from_json(const nlohmann::json &obj, Success &response);
//! Responses to the `/requestToken` endpoints
struct RequestToken
{
//! Required. The session ID. Session IDs are opaque strings that must consist entirely of the
//! characters [0-9a-zA-Z.=_-]. Their length must not exceed 255 characters and they must not be
//! empty.
std::string sid;
//! An optional field containing a URL where the client must submit the validation token to,
//! with identical parameters to the Identity Service API's POST /validate/email/submitToken
//! endpoint (without the requirement for an access token). The homeserver must send this token
//! to the user (if applicable), who should then be prompted to provide it to the client.
std::string submit_url;
};
void
from_json(const nlohmann::json &obj, RequestToken &response);
//! Different helper for parsing responses. //! Different helper for parsing responses.
namespace utils { namespace utils {
//! Multiple account_data events. //! Multiple account_data events.
......
...@@ -172,15 +172,17 @@ struct ThreePIDCred ...@@ -172,15 +172,17 @@ struct ThreePIDCred
//! Email authentication stage. //! Email authentication stage.
struct EmailIdentity struct EmailIdentity
{ {
// The 3rd party ids //! The 3rd party id
std::vector<ThreePIDCred> threepidCreds; //! See https://github.com/matrix-org/matrix-doc/pull/3471 for context.
ThreePIDCred threepidCred;
}; };
//! SMS authentication stage. //! SMS authentication stage.
struct MSISDN struct MSISDN
{ {
// The 3rd party ids //! The 3rd party id
std::vector<ThreePIDCred> threepidCreds; //! See https://github.com/matrix-org/matrix-doc/pull/3471 for context.
ThreePIDCred threepidCred;
}; };
//! Registration token authentication stage. //! Registration token authentication stage.
......
...@@ -55,10 +55,9 @@ struct AvatarUrl; ...@@ -55,10 +55,9 @@ struct AvatarUrl;
struct ClaimKeys; struct ClaimKeys;
struct ContentURI; struct ContentURI;
struct CreateRoom; struct CreateRoom;
struct Device;
struct EventId; struct EventId;
struct RoomId;
struct FilterId; struct FilterId;
struct Version;
struct GroupId; struct GroupId;
struct GroupProfile; struct GroupProfile;
struct JoinedGroups; struct JoinedGroups;
...@@ -69,18 +68,21 @@ struct LoginFlows; ...@@ -69,18 +68,21 @@ struct LoginFlows;
struct Messages; struct Messages;
struct Notifications; struct Notifications;
struct Profile; struct Profile;
struct PublicRoomVisibility;
struct PublicRooms;
struct QueryDevices;
struct QueryKeys; struct QueryKeys;
struct Register; struct Register;
struct RegistrationTokenValidity; struct RegistrationTokenValidity;
struct RequestToken;
struct RoomId;
struct Success;
struct Sync; struct Sync;
struct TurnServer; struct TurnServer;
struct UploadKeys; struct UploadKeys;
struct Version;
struct Versions; struct Versions;
struct WellKnown; struct WellKnown;
struct PublicRoomVisibility;
struct PublicRooms;
struct QueryDevices;
struct Device;
namespace backup { namespace backup {
struct SessionBackup; struct SessionBackup;
struct RoomKeysBackup; struct RoomKeysBackup;
...@@ -279,6 +281,25 @@ public: ...@@ -279,6 +281,25 @@ public:
void registration_token_validity(const std::string token, void registration_token_validity(const std::string token,
Callback<mtx::responses::RegistrationTokenValidity> cb); Callback<mtx::responses::RegistrationTokenValidity> cb);
//! Validate an unused email address.
void register_email_request_token(const requests::RequestEmailToken &r,
Callback<mtx::responses::RequestToken> cb);
//! Validate a used email address.
void verify_email_request_token(const requests::RequestEmailToken &r,
Callback<mtx::responses::RequestToken> cb);
//! Validate an unused phone number.
void register_phone_request_token(const requests::RequestMSISDNToken &r,
Callback<mtx::responses::RequestToken> cb);
//! Validate a used phone number.
void verify_phone_request_token(const requests::RequestMSISDNToken &r,
Callback<mtx::responses::RequestToken> cb);
//! Validate ownership of an email address/phone number.
void validate_submit_token(const std::string &url,
const requests::IdentitySubmitToken &r,
Callback<mtx::responses::Success>);
//! Paginate through the list of events that the user has been, //! Paginate through the list of events that the user has been,
//! or would have been notified about. //! or would have been notified about.
void notifications(uint64_t limit, void notifications(uint64_t limit,
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include "mtx/log.hpp" #include "mtx/log.hpp"
#include "mtxclient/http/client_impl.hpp" #include "mtxclient/http/client_impl.hpp"
#include <iostream>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
...@@ -917,9 +918,10 @@ Client::registration(const std::string &user, ...@@ -917,9 +918,10 @@ Client::registration(const std::string &user,
"/client/r0/register", "/client/r0/register",
request, request,
[cb, h](auto &r, RequestErr e) { [cb, h](auto &r, RequestErr e) {
if (e && e->status_code == 401) if (e && e->status_code == 401) {
std::cout << e->matrix_error.error << "\n";
h.prompt(h, e->matrix_error.unauthorized); h.prompt(h, e->matrix_error.unauthorized);
else } else
cb(r, e); cb(r, e);
}, },
false); false);
...@@ -943,6 +945,50 @@ Client::registration_token_validity(const std::string token, ...@@ -943,6 +945,50 @@ Client::registration_token_validity(const std::string token,
}); });
} }
void
Client::register_email_request_token(const requests::RequestEmailToken &r,
Callback<mtx::responses::RequestToken> cb)
{
post("/client/r0/register/email/requestToken", r, cb);
}
void
Client::verify_email_request_token(const requests::RequestEmailToken &r,
Callback<mtx::responses::RequestToken> cb)
{
post("/client/r0/account/password/email/requestToken", r, cb);
}
void
Client::register_phone_request_token(const requests::RequestMSISDNToken &r,
Callback<mtx::responses::RequestToken> cb)
{
post("/client/r0/register/msisdn/requestToken", r, cb);
}
void
Client::verify_phone_request_token(const requests::RequestMSISDNToken &r,
Callback<mtx::responses::RequestToken> cb)
{
post("/client/r0/account/password/msisdn/requestToken", r, cb);
}
void
Client::validate_submit_token(const std::string &url,
const requests::IdentitySubmitToken &r,
Callback<mtx::responses::Success> cb)
{
// some dancing to send to an arbitrary, server provided url
auto callback = prepare_callback<mtx::responses::Success>(
[cb](const mtx::responses::Success &res, HeaderFields, RequestErr err) { cb(res, err); });
p->client.post(
url,
json(r).dump(),
"application/json",
[callback](const coeurl::Request &r) {
callback(r.response_headers(), r.response(), r.error_code(), r.response_code());
},
prepare_headers(false));
}
void void
Client::send_state_event(const std::string &room_id, Client::send_state_event(const std::string &room_id,
const std::string &event_type, const std::string &event_type,
......
...@@ -84,6 +84,31 @@ to_json(json &obj, const Login &request) ...@@ -84,6 +84,31 @@ to_json(json &obj, const Login &request)
obj["type"] = request.type; obj["type"] = request.type;
} }
void
to_json(json &obj, const RequestEmailToken &request)
{
obj["client_secret"] = request.client_secret;
obj["email"] = request.email;
obj["send_attempt"] = request.send_attempt;
}
void
to_json(json &obj, const RequestMSISDNToken &request)
{
obj["client_secret"] = request.client_secret;
obj["country"] = request.country;
obj["phone_number"] = request.phone_number;
obj["send_attempt"] = request.send_attempt;
}
void
to_json(json &obj, const IdentitySubmitToken &request)
{
obj["sid"] = request.sid;
obj["client_secret"] = request.client_secret;
obj["token"] = request.token;
}
void void
to_json(json &obj, const AvatarUrl &request) to_json(json &obj, const AvatarUrl &request)
{ {
......
...@@ -59,6 +59,21 @@ from_json(const nlohmann::json &obj, Version &response) ...@@ -59,6 +59,21 @@ from_json(const nlohmann::json &obj, Version &response)
response.version = obj.at("version"); response.version = obj.at("version");
} }
void
from_json(const nlohmann::json &obj, Success &success)
{
success.success = obj.at("success");
}
void
from_json(const nlohmann::json &obj, RequestToken &r)
{
r.sid = obj.at("sid");
if (obj.contains("submit_url"))
r.submit_url = obj.at("submit_url");
}
namespace utils { namespace utils {
void void
......
...@@ -110,12 +110,12 @@ to_json(nlohmann::json &obj, const Auth &auth) ...@@ -110,12 +110,12 @@ to_json(nlohmann::json &obj, const Auth &auth)
obj["txn_id"] = token.txn_id; obj["txn_id"] = token.txn_id;
}, },
[&obj](const auth::EmailIdentity &id) { [&obj](const auth::EmailIdentity &id) {
obj["type"] = auth_types::email_identity; obj["type"] = auth_types::email_identity;
obj["threepidCreds"] = id.threepidCreds; obj["threepid_creds"] = id.threepidCred;
}, },
[&obj](const auth::MSISDN &id) { [&obj](const auth::MSISDN &id) {
obj["type"] = auth_types::msisdn; obj["type"] = auth_types::msisdn;
obj["threepidCreds"] = id.threepidCreds; obj["threepid_creds"] = id.threepidCred;
}, },
[&obj](const auth::RegistrationToken &registration_token) { [&obj](const auth::RegistrationToken &registration_token) {
obj["type"] = auth_types::registration_token; obj["type"] = auth_types::registration_token;
......
...@@ -225,58 +225,48 @@ TEST(Requests, UserInteractiveAuth) ...@@ -225,58 +225,48 @@ TEST(Requests, UserInteractiveAuth)
"session": "<session ID>" "session": "<session ID>"
})"_json); })"_json);
a.content = auth::EmailIdentity{{ a.content =
{"<identity server session id>", auth::EmailIdentity{"<identity server session id>",
"<identity server client secret>", "<identity server client secret>",
"<url of identity server authed with, e.g. 'matrix.org:8090'>", "<url of identity server authed with, e.g. 'matrix.org:8090'>",
"<access token previously registered with the identity server>"}, "<access token previously registered with the identity server>"};
}};
EXPECT_EQ(nlohmann::json(a), R"({ EXPECT_EQ(nlohmann::json(a), R"({
"type": "m.login.email.identity", "type": "m.login.email.identity",
"threepidCreds": [ "threepid_creds": {
{ "sid": "<identity server session id>",
"sid": "<identity server session id>", "client_secret": "<identity server client secret>",
"client_secret": "<identity server client secret>", "id_server": "<url of identity server authed with, e.g. 'matrix.org:8090'>",
"id_server": "<url of identity server authed with, e.g. 'matrix.org:8090'>", "id_access_token": "<access token previously registered with the identity server>"
"id_access_token": "<access token previously registered with the identity server>" },
}
],
"session": "<session ID>" "session": "<session ID>"
})"_json); })"_json);
a.content = auth::MSISDN{{ a.content = auth::MSISDN{"<identity server session id>",
{"<identity server session id>", "<identity server client secret>",
"<identity server client secret>", "<url of identity server authed with, e.g. 'matrix.org:8090'>",
"<url of identity server authed with, e.g. 'matrix.org:8090'>", "<access token previously registered with the identity server>"};
"<access token previously registered with the identity server>"},
}};
EXPECT_EQ(nlohmann::json(a), R"({ EXPECT_EQ(nlohmann::json(a), R"({
"type": "m.login.msisdn", "type": "m.login.msisdn",
"threepidCreds": [ "threepid_creds": {
{ "sid": "<identity server session id>",
"sid": "<identity server session id>", "client_secret": "<identity server client secret>",
"client_secret": "<identity server client secret>", "id_server": "<url of identity server authed with, e.g. 'matrix.org:8090'>",
"id_server": "<url of identity server authed with, e.g. 'matrix.org:8090'>", "id_access_token": "<access token previously registered with the identity server>"
"id_access_token": "<access token previously registered with the identity server>" },
}
],
"session": "<session ID>" "session": "<session ID>"
})"_json); })"_json);
a.content = auth::MSISDN{{ a.content =
{"<identity server session id>", "<identity server client secret>", "", ""}, auth::MSISDN{"<identity server session id>", "<identity server client secret>", "", ""};
}};
EXPECT_EQ(nlohmann::json(a), R"({ EXPECT_EQ(nlohmann::json(a), R"({
"type": "m.login.msisdn", "type": "m.login.msisdn",
"threepidCreds": [ "threepid_creds": {
{ "sid": "<identity server session id>",
"sid": "<identity server session id>", "client_secret": "<identity server client secret>"
"client_secret": "<identity server client secret>" },
}
],
"session": "<session ID>" "session": "<session ID>"
})"_json); })"_json);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment