use ruma_api::ruma_api;
use ruma_common::thirdparty::Medium;
use ruma_identifiers::{DeviceId, DeviceIdBox, ServerNameBox, UserId};
use ruma_serde::Outgoing;
use serde::{Deserialize, Serialize};
ruma_api! {
metadata: {
description: "Login to the homeserver.",
method: POST,
name: "login",
path: "/_matrix/client/r0/login",
rate_limited: true,
authentication: None,
}
request: {
#[serde(flatten)]
pub login_info: LoginInfo<'a>,
#[serde(skip_serializing_if = "Option::is_none")]
pub device_id: Option<&'a DeviceId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub initial_device_display_name: Option<&'a str>,
}
response: {
pub user_id: UserId,
pub access_token: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub home_server: Option<ServerNameBox>,
pub device_id: DeviceIdBox,
pub well_known: Option<DiscoveryInfo>,
}
error: crate::Error
}
impl<'a> Request<'a> {
pub fn new(login_info: LoginInfo<'a>) -> Self {
Self { login_info, device_id: None, initial_device_display_name: None }
}
}
impl Response {
pub fn new(user_id: UserId, access_token: String, device_id: DeviceIdBox) -> Self {
Self { user_id, access_token, home_server: None, device_id, well_known: None }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Outgoing, Serialize)]
#[serde(from = "user_serde::IncomingUserIdentifier", into = "user_serde::UserIdentifier")]
pub enum UserIdentifier<'a> {
MatrixId(&'a str),
ThirdPartyId {
address: &'a str,
medium: Medium,
},
PhoneNumber {
country: &'a str,
phone: &'a str,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Outgoing, Serialize)]
#[serde(tag = "type")]
pub enum LoginInfo<'a> {
#[serde(rename = "m.login.password")]
Password {
identifier: UserIdentifier<'a>,
password: &'a str,
},
#[serde(rename = "m.login.token")]
Token {
token: &'a str,
},
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DiscoveryInfo {
#[serde(rename = "m.homeserver")]
pub homeserver: HomeserverInfo,
#[serde(rename = "m.identity_server")]
pub identity_server: Option<IdentityServerInfo>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct HomeserverInfo {
pub base_url: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct IdentityServerInfo {
pub base_url: String,
}
mod user_serde;
#[cfg(test)]
mod tests {
use matches::assert_matches;
use serde_json::{from_value as from_json_value, json};
use super::{IncomingLoginInfo, IncomingUserIdentifier};
#[test]
fn deserialize_login_type() {
assert_matches!(
from_json_value(json!({
"type": "m.login.password",
"identifier": {
"type": "m.id.user",
"user": "cheeky_monkey"
},
"password": "ilovebananas"
}))
.unwrap(),
IncomingLoginInfo::Password { identifier: IncomingUserIdentifier::MatrixId(user), password }
if user == "cheeky_monkey" && password == "ilovebananas"
);
assert_matches!(
from_json_value(json!({
"type": "m.login.token",
"token": "1234567890abcdef"
}))
.unwrap(),
IncomingLoginInfo::Token { token }
if token == "1234567890abcdef"
);
}
#[test]
fn deserialize_user() {
assert_matches!(
from_json_value(json!({
"type": "m.id.user",
"user": "cheeky_monkey"
}))
.unwrap(),
IncomingUserIdentifier::MatrixId(id)
if id == "cheeky_monkey"
);
}
#[test]
#[cfg(feature = "client")]
fn serialize_login_request_body() {
use ruma_api::OutgoingRequest;
use serde_json::Value as JsonValue;
use super::{LoginInfo, Medium, Request, UserIdentifier};
let req: http::Request<Vec<u8>> = Request {
login_info: LoginInfo::Token { token: "0xdeadbeef" },
device_id: None,
initial_device_display_name: Some("test"),
}
.try_into_http_request("https://homeserver.tld", None)
.unwrap();
let req_body_value: JsonValue = serde_json::from_slice(req.body()).unwrap();
assert_eq!(
req_body_value,
json!({
"type": "m.login.token",
"token": "0xdeadbeef",
"initial_device_display_name": "test",
})
);
let req: http::Request<Vec<u8>> = Request {
login_info: LoginInfo::Password {
identifier: UserIdentifier::ThirdPartyId {
address: "hello@example.com",
medium: Medium::Email,
},
password: "deadbeef",
},
device_id: None,
initial_device_display_name: Some("test"),
}
.try_into_http_request("https://homeserver.tld", None)
.unwrap();
let req_body_value: JsonValue = serde_json::from_slice(req.body()).unwrap();
assert_eq!(
req_body_value,
json!({
"identifier": {
"type": "m.id.thirdparty",
"medium": "email",
"address": "hello@example.com"
},
"type": "m.login.password",
"password": "deadbeef",
"initial_device_display_name": "test",
})
);
}
}