mirror of
https://github.com/matrix-construct/construct
synced 2024-11-29 10:12:39 +01:00
ircd:Ⓜ️/modules/client: Add device ID generation; use token/password helpers; various comments/cleanup.
This commit is contained in:
parent
6ece5db391
commit
eab4aef7e6
4 changed files with 128 additions and 63 deletions
|
@ -14,6 +14,8 @@
|
|||
namespace ircd::m
|
||||
{
|
||||
struct user;
|
||||
|
||||
bool exists(const id::user &);
|
||||
}
|
||||
|
||||
struct ircd::m::user
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include <ircd/m/m.h>
|
||||
|
||||
/// ID of the room which indexes all users (an instance of the room is
|
||||
/// provided below).
|
||||
const ircd::m::room::id::buf
|
||||
users_room_id
|
||||
{
|
||||
|
@ -27,17 +29,19 @@ ircd::m::user::users
|
|||
users_room_id
|
||||
};
|
||||
|
||||
/// The tokens room serves as a key-value lookup for various tokens to
|
||||
/// users, etc. It primarily serves to store access tokens for users. This
|
||||
/// is a separate room from the users room because in the future it may
|
||||
/// have an optimized configuration as well as being more easily cleared.
|
||||
///
|
||||
/// ID of the room which stores ephemeral tokens (an instance of the room is
|
||||
/// provided below).
|
||||
const ircd::m::room::id::buf
|
||||
tokens_room_id
|
||||
{
|
||||
"tokens", ircd::my_host()
|
||||
};
|
||||
|
||||
/// The tokens room serves as a key-value lookup for various tokens to
|
||||
/// users, etc. It primarily serves to store access tokens for users. This
|
||||
/// is a separate room from the users room because in the future it may
|
||||
/// have an optimized configuration as well as being more easily cleared.
|
||||
///
|
||||
ircd::m::room
|
||||
ircd::m::user::tokens
|
||||
{
|
||||
|
@ -92,22 +96,16 @@ void
|
|||
ircd::m::user::password(const string_view &password)
|
||||
try
|
||||
{
|
||||
//TODO: ADD SALT
|
||||
const sha256::buf hash
|
||||
char buf[64];
|
||||
const auto supplied
|
||||
{
|
||||
sha256{password}
|
||||
gen_password_hash(buf, password)
|
||||
};
|
||||
|
||||
char b64[64];
|
||||
const auto digest
|
||||
const user::room user_room{*this};
|
||||
send(user_room, user_id, "ircd.password", user_id,
|
||||
{
|
||||
b64encode_unpadded(b64, hash)
|
||||
};
|
||||
|
||||
const auto room_id{this->room_id()};
|
||||
send(room_id, user_id, "ircd.password", user_id,
|
||||
{
|
||||
{ "sha256", digest }
|
||||
{ "sha256", supplied }
|
||||
});
|
||||
}
|
||||
catch(const m::ALREADY_MEMBER &e)
|
||||
|
@ -119,24 +117,18 @@ catch(const m::ALREADY_MEMBER &e)
|
|||
}
|
||||
|
||||
bool
|
||||
ircd::m::user::is_password(const string_view &supplied_password)
|
||||
ircd::m::user::is_password(const string_view &password)
|
||||
const
|
||||
{
|
||||
//TODO: ADD SALT
|
||||
const sha256::buf hash
|
||||
char buf[64];
|
||||
const auto supplied
|
||||
{
|
||||
sha256{supplied_password}
|
||||
};
|
||||
|
||||
char b64[64];
|
||||
const auto supplied_hash
|
||||
{
|
||||
b64encode_unpadded(b64, hash)
|
||||
gen_password_hash(buf, password)
|
||||
};
|
||||
|
||||
bool ret{false};
|
||||
const auto room_id{this->room_id()};
|
||||
m::room{room_id}.get("ircd.password", user_id, [&supplied_hash, &ret]
|
||||
const user::room user_room{*this};
|
||||
user_room.get("ircd.password", user_id, [&supplied, &ret]
|
||||
(const m::event &event)
|
||||
{
|
||||
const json::object &content
|
||||
|
@ -144,12 +136,12 @@ const
|
|||
json::at<"content"_>(event)
|
||||
};
|
||||
|
||||
const auto &correct_hash
|
||||
const auto &correct
|
||||
{
|
||||
unquote(content.at("sha256"))
|
||||
};
|
||||
|
||||
ret = supplied_hash == correct_hash;
|
||||
ret = supplied == correct;
|
||||
});
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -28,29 +28,56 @@ login_resource
|
|||
|
||||
namespace { namespace name
|
||||
{
|
||||
constexpr const auto password{"password"};
|
||||
constexpr const auto medium{"medium"};
|
||||
constexpr const auto type{"type"};
|
||||
constexpr const auto user{"user"};
|
||||
constexpr const auto medium{"medium"};
|
||||
constexpr const auto address{"address"};
|
||||
constexpr const auto password{"password"};
|
||||
constexpr const auto token{"token"};
|
||||
constexpr const auto device_id{"device_id"};
|
||||
constexpr const auto initial_device_display_name{"initial_device_display_name"};
|
||||
}}
|
||||
|
||||
struct body
|
||||
:json::tuple
|
||||
<
|
||||
json::property<name::password, string_view>,
|
||||
json::property<name::medium, time_t>,
|
||||
/// Required. The login type being used. One of: ["m.login.password",
|
||||
/// "m.login.token"]
|
||||
json::property<name::type, string_view>,
|
||||
|
||||
/// The fully qualified user ID or just local part of the user ID, to
|
||||
/// log in.
|
||||
json::property<name::user, string_view>,
|
||||
json::property<name::address, string_view>
|
||||
|
||||
/// When logging in using a third party identifier, the medium of the
|
||||
/// identifier. Must be 'email'.
|
||||
json::property<name::medium, string_view>,
|
||||
|
||||
/// Third party identifier for the user.
|
||||
json::property<name::address, string_view>,
|
||||
|
||||
/// Required when type is m.login.password. The user's password.
|
||||
json::property<name::password, string_view>,
|
||||
|
||||
/// Required when type is m.login.token. The login token.
|
||||
json::property<name::token, string_view>,
|
||||
|
||||
/// ID of the client device. If this does not correspond to a known client
|
||||
/// device, a new device will be created. The server will auto-generate a
|
||||
/// device_id if this is not specified.
|
||||
json::property<name::device_id, string_view>,
|
||||
|
||||
/// A display name to assign to the newly-created device. Ignored if
|
||||
/// device_id corresponds to a known device.
|
||||
json::property<name::initial_device_display_name, string_view>
|
||||
>
|
||||
{
|
||||
using super_type::tuple;
|
||||
};
|
||||
|
||||
resource::response
|
||||
post_login_password(client &client,
|
||||
const resource::request::object<body> &request)
|
||||
post__login_password(client &client,
|
||||
const resource::request::object<body> &request)
|
||||
{
|
||||
// Build a canonical MXID from a the user field
|
||||
const m::id::user::buf user_id
|
||||
|
@ -80,13 +107,22 @@ post_login_password(client &client,
|
|||
"Access denied."
|
||||
};
|
||||
|
||||
// Generate the access token
|
||||
static constexpr const auto token_len{127};
|
||||
static const auto token_dict{rand::dict::alpha};
|
||||
char token_buf[token_len + 1];
|
||||
const auto requested_device_id
|
||||
{
|
||||
unquote(json::get<"device_id"_>(request))
|
||||
};
|
||||
|
||||
const m::id::device device_id
|
||||
{
|
||||
requested_device_id?
|
||||
m::id::device::buf{requested_device_id, my_host()}:
|
||||
m::id::device::buf{m::id::generate, my_host()}
|
||||
};
|
||||
|
||||
char access_token_buf[32];
|
||||
const string_view access_token
|
||||
{
|
||||
rand::string(token_dict, token_len, token_buf, sizeof(token_buf))
|
||||
m::user::gen_access_token(access_token_buf)
|
||||
};
|
||||
|
||||
// Log the user in by issuing an event in the tokens room containing
|
||||
|
@ -95,49 +131,51 @@ post_login_password(client &client,
|
|||
m::send(m::user::tokens, user_id, "ircd.access_token", access_token,
|
||||
{
|
||||
{ "ip", string(remote(client)) },
|
||||
{ "device", "unknown" },
|
||||
{ "device", device_id },
|
||||
});
|
||||
|
||||
// Send response to user
|
||||
return resource::response
|
||||
{
|
||||
client,
|
||||
client, json::members
|
||||
{
|
||||
{ "user_id", user_id },
|
||||
{ "home_server", my_host() },
|
||||
{ "access_token", access_token },
|
||||
{ "device_id", device_id },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
resource::response
|
||||
post_login(client &client, const resource::request::object<body> &request)
|
||||
post_login(client &client,
|
||||
const resource::request::object<body> &request)
|
||||
{
|
||||
// x.x.x Required. The login type being used.
|
||||
// Currently only "m.login.password" is supported.
|
||||
const auto type
|
||||
const auto &type
|
||||
{
|
||||
unquote(at<"type"_>(request))
|
||||
};
|
||||
|
||||
if(type == "m.login.password")
|
||||
return post_login_password(client, request);
|
||||
else
|
||||
throw m::error
|
||||
{
|
||||
"M_UNSUPPORTED", "Login type is not supported."
|
||||
};
|
||||
return post__login_password(client, request);
|
||||
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Login type is not supported."
|
||||
};
|
||||
}
|
||||
|
||||
resource::method method_post
|
||||
resource::method
|
||||
method_post
|
||||
{
|
||||
login_resource, "POST", post_login
|
||||
};
|
||||
|
||||
resource::response
|
||||
get_login(client &client, const resource::request &request)
|
||||
get_login(client &client,
|
||||
const resource::request &request)
|
||||
{
|
||||
json::member login_password
|
||||
const json::member login_password
|
||||
{
|
||||
"type", "m.login.password"
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace { namespace name
|
|||
constexpr const auto bind_email {"bind_email"};
|
||||
constexpr const auto password {"password"};
|
||||
constexpr const auto auth {"auth"};
|
||||
constexpr const auto device_id {"device_id"};
|
||||
}}
|
||||
|
||||
struct body
|
||||
|
@ -30,7 +31,8 @@ struct body
|
|||
json::property<name::username, string_view>,
|
||||
json::property<name::bind_email, bool>,
|
||||
json::property<name::password, string_view>,
|
||||
json::property<name::auth, json::object>
|
||||
json::property<name::auth, json::object>,
|
||||
json::property<name::device_id, string_view>
|
||||
>
|
||||
{
|
||||
using super_type::tuple;
|
||||
|
@ -45,13 +47,13 @@ handle_post_kind_user(client &client,
|
|||
try
|
||||
{
|
||||
// 3.3.1 Additional authentication information for the user-interactive authentication API.
|
||||
const json::object auth
|
||||
const json::object &auth
|
||||
{
|
||||
json::get<"auth"_>(request)
|
||||
};
|
||||
|
||||
// 3.3.1 Required. The login type that the client is attempting to complete.
|
||||
const string_view type
|
||||
// 3.3.1 The login type that the client is attempting to complete.
|
||||
const string_view &type
|
||||
{
|
||||
!empty(auth)? unquote(auth.at("type")) : string_view{}
|
||||
};
|
||||
|
@ -87,6 +89,21 @@ try
|
|||
unquote(at<"password"_>(request))
|
||||
};
|
||||
|
||||
// (r0.3.0) 3.4.1 ID of the client device. If this does not correspond to a
|
||||
// known client device, a new device will be created. The server will auto-
|
||||
// generate a device_id if this is not specified.
|
||||
const auto requested_device_id
|
||||
{
|
||||
unquote(json::get<"device_id"_>(request))
|
||||
};
|
||||
|
||||
const m::id::device device_id
|
||||
{
|
||||
requested_device_id?
|
||||
m::id::device::buf{requested_device_id, my_host()}:
|
||||
m::id::device::buf{m::id::generate, my_host()}
|
||||
};
|
||||
|
||||
// 3.3.1 If true, the server binds the email used for authentication to the
|
||||
// Matrix ID with the ID Server. Defaults to false.
|
||||
const auto &bind_email
|
||||
|
@ -117,6 +134,21 @@ try
|
|||
// m.login.password
|
||||
user.password(password);
|
||||
|
||||
char access_token_buf[32];
|
||||
const string_view access_token
|
||||
{
|
||||
m::user::gen_access_token(access_token_buf)
|
||||
};
|
||||
|
||||
// Log the user in by issuing an event in the tokens room containing
|
||||
// the generated token. When this call completes without throwing the
|
||||
// access_token will be committed and the user will be logged in.
|
||||
m::send(m::user::tokens, user_id, "ircd.access_token", access_token,
|
||||
{
|
||||
{ "ip", string(remote(client)) },
|
||||
{ "device", device_id },
|
||||
});
|
||||
|
||||
// Send response to user
|
||||
return resource::response
|
||||
{
|
||||
|
@ -124,7 +156,8 @@ try
|
|||
{
|
||||
{ "user_id", user_id },
|
||||
{ "home_server", my_host() },
|
||||
// { "access_token", access_token },
|
||||
{ "access_token", access_token },
|
||||
{ "device_id", device_id },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue