mirror of
https://github.com/matrix-construct/construct
synced 2024-11-29 18:22:50 +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
|
namespace ircd::m
|
||||||
{
|
{
|
||||||
struct user;
|
struct user;
|
||||||
|
|
||||||
|
bool exists(const id::user &);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ircd::m::user
|
struct ircd::m::user
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#include <ircd/m/m.h>
|
#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
|
const ircd::m::room::id::buf
|
||||||
users_room_id
|
users_room_id
|
||||||
{
|
{
|
||||||
|
@ -27,17 +29,19 @@ ircd::m::user::users
|
||||||
users_room_id
|
users_room_id
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The tokens room serves as a key-value lookup for various tokens to
|
/// ID of the room which stores ephemeral tokens (an instance of the room is
|
||||||
/// users, etc. It primarily serves to store access tokens for users. This
|
/// provided below).
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
const ircd::m::room::id::buf
|
const ircd::m::room::id::buf
|
||||||
tokens_room_id
|
tokens_room_id
|
||||||
{
|
{
|
||||||
"tokens", ircd::my_host()
|
"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::room
|
||||||
ircd::m::user::tokens
|
ircd::m::user::tokens
|
||||||
{
|
{
|
||||||
|
@ -92,22 +96,16 @@ void
|
||||||
ircd::m::user::password(const string_view &password)
|
ircd::m::user::password(const string_view &password)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//TODO: ADD SALT
|
char buf[64];
|
||||||
const sha256::buf hash
|
const auto supplied
|
||||||
{
|
{
|
||||||
sha256{password}
|
gen_password_hash(buf, password)
|
||||||
};
|
};
|
||||||
|
|
||||||
char b64[64];
|
const user::room user_room{*this};
|
||||||
const auto digest
|
send(user_room, user_id, "ircd.password", user_id,
|
||||||
{
|
{
|
||||||
b64encode_unpadded(b64, hash)
|
{ "sha256", supplied }
|
||||||
};
|
|
||||||
|
|
||||||
const auto room_id{this->room_id()};
|
|
||||||
send(room_id, user_id, "ircd.password", user_id,
|
|
||||||
{
|
|
||||||
{ "sha256", digest }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch(const m::ALREADY_MEMBER &e)
|
catch(const m::ALREADY_MEMBER &e)
|
||||||
|
@ -119,24 +117,18 @@ catch(const m::ALREADY_MEMBER &e)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ircd::m::user::is_password(const string_view &supplied_password)
|
ircd::m::user::is_password(const string_view &password)
|
||||||
const
|
const
|
||||||
{
|
{
|
||||||
//TODO: ADD SALT
|
char buf[64];
|
||||||
const sha256::buf hash
|
const auto supplied
|
||||||
{
|
{
|
||||||
sha256{supplied_password}
|
gen_password_hash(buf, password)
|
||||||
};
|
|
||||||
|
|
||||||
char b64[64];
|
|
||||||
const auto supplied_hash
|
|
||||||
{
|
|
||||||
b64encode_unpadded(b64, hash)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ret{false};
|
bool ret{false};
|
||||||
const auto room_id{this->room_id()};
|
const user::room user_room{*this};
|
||||||
m::room{room_id}.get("ircd.password", user_id, [&supplied_hash, &ret]
|
user_room.get("ircd.password", user_id, [&supplied, &ret]
|
||||||
(const m::event &event)
|
(const m::event &event)
|
||||||
{
|
{
|
||||||
const json::object &content
|
const json::object &content
|
||||||
|
@ -144,12 +136,12 @@ const
|
||||||
json::at<"content"_>(event)
|
json::at<"content"_>(event)
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto &correct_hash
|
const auto &correct
|
||||||
{
|
{
|
||||||
unquote(content.at("sha256"))
|
unquote(content.at("sha256"))
|
||||||
};
|
};
|
||||||
|
|
||||||
ret = supplied_hash == correct_hash;
|
ret = supplied == correct;
|
||||||
});
|
});
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -28,29 +28,56 @@ login_resource
|
||||||
|
|
||||||
namespace { namespace name
|
namespace { namespace name
|
||||||
{
|
{
|
||||||
constexpr const auto password{"password"};
|
|
||||||
constexpr const auto medium{"medium"};
|
|
||||||
constexpr const auto type{"type"};
|
constexpr const auto type{"type"};
|
||||||
constexpr const auto user{"user"};
|
constexpr const auto user{"user"};
|
||||||
|
constexpr const auto medium{"medium"};
|
||||||
constexpr const auto address{"address"};
|
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
|
struct body
|
||||||
:json::tuple
|
:json::tuple
|
||||||
<
|
<
|
||||||
json::property<name::password, string_view>,
|
/// Required. The login type being used. One of: ["m.login.password",
|
||||||
json::property<name::medium, time_t>,
|
/// "m.login.token"]
|
||||||
json::property<name::type, string_view>,
|
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::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;
|
using super_type::tuple;
|
||||||
};
|
};
|
||||||
|
|
||||||
resource::response
|
resource::response
|
||||||
post_login_password(client &client,
|
post__login_password(client &client,
|
||||||
const resource::request::object<body> &request)
|
const resource::request::object<body> &request)
|
||||||
{
|
{
|
||||||
// Build a canonical MXID from a the user field
|
// Build a canonical MXID from a the user field
|
||||||
const m::id::user::buf user_id
|
const m::id::user::buf user_id
|
||||||
|
@ -80,13 +107,22 @@ post_login_password(client &client,
|
||||||
"Access denied."
|
"Access denied."
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate the access token
|
const auto requested_device_id
|
||||||
static constexpr const auto token_len{127};
|
{
|
||||||
static const auto token_dict{rand::dict::alpha};
|
unquote(json::get<"device_id"_>(request))
|
||||||
char token_buf[token_len + 1];
|
};
|
||||||
|
|
||||||
|
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
|
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
|
// 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,
|
m::send(m::user::tokens, user_id, "ircd.access_token", access_token,
|
||||||
{
|
{
|
||||||
{ "ip", string(remote(client)) },
|
{ "ip", string(remote(client)) },
|
||||||
{ "device", "unknown" },
|
{ "device", device_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send response to user
|
// Send response to user
|
||||||
return resource::response
|
return resource::response
|
||||||
{
|
{
|
||||||
client,
|
client, json::members
|
||||||
{
|
{
|
||||||
{ "user_id", user_id },
|
{ "user_id", user_id },
|
||||||
{ "home_server", my_host() },
|
{ "home_server", my_host() },
|
||||||
{ "access_token", access_token },
|
{ "access_token", access_token },
|
||||||
|
{ "device_id", device_id },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
resource::response
|
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.
|
const auto &type
|
||||||
// Currently only "m.login.password" is supported.
|
|
||||||
const auto type
|
|
||||||
{
|
{
|
||||||
unquote(at<"type"_>(request))
|
unquote(at<"type"_>(request))
|
||||||
};
|
};
|
||||||
|
|
||||||
if(type == "m.login.password")
|
if(type == "m.login.password")
|
||||||
return post_login_password(client, request);
|
return post__login_password(client, request);
|
||||||
else
|
|
||||||
throw m::error
|
throw m::UNSUPPORTED
|
||||||
{
|
{
|
||||||
"M_UNSUPPORTED", "Login type is not supported."
|
"Login type is not supported."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
resource::method method_post
|
resource::method
|
||||||
|
method_post
|
||||||
{
|
{
|
||||||
login_resource, "POST", post_login
|
login_resource, "POST", post_login
|
||||||
};
|
};
|
||||||
|
|
||||||
resource::response
|
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"
|
"type", "m.login.password"
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,7 @@ namespace { namespace name
|
||||||
constexpr const auto bind_email {"bind_email"};
|
constexpr const auto bind_email {"bind_email"};
|
||||||
constexpr const auto password {"password"};
|
constexpr const auto password {"password"};
|
||||||
constexpr const auto auth {"auth"};
|
constexpr const auto auth {"auth"};
|
||||||
|
constexpr const auto device_id {"device_id"};
|
||||||
}}
|
}}
|
||||||
|
|
||||||
struct body
|
struct body
|
||||||
|
@ -30,7 +31,8 @@ struct body
|
||||||
json::property<name::username, string_view>,
|
json::property<name::username, string_view>,
|
||||||
json::property<name::bind_email, bool>,
|
json::property<name::bind_email, bool>,
|
||||||
json::property<name::password, string_view>,
|
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;
|
using super_type::tuple;
|
||||||
|
@ -45,13 +47,13 @@ handle_post_kind_user(client &client,
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 3.3.1 Additional authentication information for the user-interactive authentication API.
|
// 3.3.1 Additional authentication information for the user-interactive authentication API.
|
||||||
const json::object auth
|
const json::object &auth
|
||||||
{
|
{
|
||||||
json::get<"auth"_>(request)
|
json::get<"auth"_>(request)
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3.3.1 Required. The login type that the client is attempting to complete.
|
// 3.3.1 The login type that the client is attempting to complete.
|
||||||
const string_view type
|
const string_view &type
|
||||||
{
|
{
|
||||||
!empty(auth)? unquote(auth.at("type")) : string_view{}
|
!empty(auth)? unquote(auth.at("type")) : string_view{}
|
||||||
};
|
};
|
||||||
|
@ -87,6 +89,21 @@ try
|
||||||
unquote(at<"password"_>(request))
|
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
|
// 3.3.1 If true, the server binds the email used for authentication to the
|
||||||
// Matrix ID with the ID Server. Defaults to false.
|
// Matrix ID with the ID Server. Defaults to false.
|
||||||
const auto &bind_email
|
const auto &bind_email
|
||||||
|
@ -117,6 +134,21 @@ try
|
||||||
// m.login.password
|
// m.login.password
|
||||||
user.password(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
|
// Send response to user
|
||||||
return resource::response
|
return resource::response
|
||||||
{
|
{
|
||||||
|
@ -124,7 +156,8 @@ try
|
||||||
{
|
{
|
||||||
{ "user_id", user_id },
|
{ "user_id", user_id },
|
||||||
{ "home_server", my_host() },
|
{ "home_server", my_host() },
|
||||||
// { "access_token", access_token },
|
{ "access_token", access_token },
|
||||||
|
{ "device_id", device_id },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue