1use std::{borrow::Cow, fmt};
4
5use ruma_common::{
6 OwnedClientSecret, OwnedSessionId, OwnedUserId, serde::JsonObject, thirdparty::Medium,
7};
8use serde::{Deserialize, Serialize, de::DeserializeOwned};
9use serde_json::Value as JsonValue;
10
11mod data_serde;
12
13use super::AuthType;
14use crate::PrivOwnedStr;
15
16#[derive(Clone, Serialize)]
18#[non_exhaustive]
19#[serde(untagged)]
20pub enum AuthData {
21 Password(Password),
23
24 ReCaptcha(ReCaptcha),
26
27 EmailIdentity(EmailIdentity),
29
30 Msisdn(Msisdn),
32
33 Dummy(Dummy),
35
36 RegistrationToken(RegistrationToken),
38
39 FallbackAcknowledgement(FallbackAcknowledgement),
41
42 Terms(Terms),
46
47 OAuth(OAuth),
52
53 #[doc(hidden)]
55 _Custom(CustomAuthData),
56}
57
58impl AuthData {
59 pub fn new(
70 auth_type: &str,
71 session: Option<String>,
72 data: JsonObject,
73 ) -> serde_json::Result<Self> {
74 fn deserialize_variant<T: DeserializeOwned>(
75 session: Option<String>,
76 mut obj: JsonObject,
77 ) -> serde_json::Result<T> {
78 if let Some(session) = session {
79 obj.insert("session".into(), session.into());
80 }
81 serde_json::from_value(JsonValue::Object(obj))
82 }
83
84 Ok(match auth_type {
85 "m.login.password" => Self::Password(deserialize_variant(session, data)?),
86 "m.login.recaptcha" => Self::ReCaptcha(deserialize_variant(session, data)?),
87 "m.login.email.identity" => Self::EmailIdentity(deserialize_variant(session, data)?),
88 "m.login.msisdn" => Self::Msisdn(deserialize_variant(session, data)?),
89 "m.login.dummy" => Self::Dummy(deserialize_variant(session, data)?),
90 "m.registration_token" => Self::RegistrationToken(deserialize_variant(session, data)?),
91 "m.login.terms" => Self::Terms(deserialize_variant(session, data)?),
92 "m.oauth" | "org.matrix.cross_signing_reset" => {
93 Self::OAuth(deserialize_variant(session, data)?)
94 }
95 _ => {
96 Self::_Custom(CustomAuthData { auth_type: auth_type.into(), session, extra: data })
97 }
98 })
99 }
100
101 pub fn fallback_acknowledgement(session: String) -> Self {
103 Self::FallbackAcknowledgement(FallbackAcknowledgement::new(session))
104 }
105
106 pub fn auth_type(&self) -> Option<AuthType> {
108 match self {
109 Self::Password(_) => Some(AuthType::Password),
110 Self::ReCaptcha(_) => Some(AuthType::ReCaptcha),
111 Self::EmailIdentity(_) => Some(AuthType::EmailIdentity),
112 Self::Msisdn(_) => Some(AuthType::Msisdn),
113 Self::Dummy(_) => Some(AuthType::Dummy),
114 Self::RegistrationToken(_) => Some(AuthType::RegistrationToken),
115 Self::FallbackAcknowledgement(_) => None,
116 Self::Terms(_) => Some(AuthType::Terms),
117 Self::OAuth(_) => Some(AuthType::OAuth),
118 Self::_Custom(c) => Some(AuthType::_Custom(PrivOwnedStr(c.auth_type.as_str().into()))),
119 }
120 }
121
122 pub fn session(&self) -> Option<&str> {
124 match self {
125 Self::Password(x) => x.session.as_deref(),
126 Self::ReCaptcha(x) => x.session.as_deref(),
127 Self::EmailIdentity(x) => x.session.as_deref(),
128 Self::Msisdn(x) => x.session.as_deref(),
129 Self::Dummy(x) => x.session.as_deref(),
130 Self::RegistrationToken(x) => x.session.as_deref(),
131 Self::FallbackAcknowledgement(x) => Some(&x.session),
132 Self::Terms(x) => x.session.as_deref(),
133 Self::OAuth(x) => x.session.as_deref(),
134 Self::_Custom(x) => x.session.as_deref(),
135 }
136 }
137
138 pub fn data(&self) -> Cow<'_, JsonObject> {
146 fn serialize<T: Serialize>(obj: T) -> JsonObject {
147 match serde_json::to_value(obj).expect("auth data serialization to succeed") {
148 JsonValue::Object(obj) => obj,
149 _ => panic!("all auth data variants must serialize to objects"),
150 }
151 }
152
153 match self {
154 Self::Password(x) => Cow::Owned(serialize(Password {
155 identifier: x.identifier.clone(),
156 password: x.password.clone(),
157 session: None,
158 })),
159 Self::ReCaptcha(x) => {
160 Cow::Owned(serialize(ReCaptcha { response: x.response.clone(), session: None }))
161 }
162 Self::EmailIdentity(x) => Cow::Owned(serialize(EmailIdentity {
163 thirdparty_id_creds: x.thirdparty_id_creds.clone(),
164 session: None,
165 })),
166 Self::Msisdn(x) => Cow::Owned(serialize(Msisdn {
167 thirdparty_id_creds: x.thirdparty_id_creds.clone(),
168 session: None,
169 })),
170 Self::RegistrationToken(x) => {
171 Cow::Owned(serialize(RegistrationToken { token: x.token.clone(), session: None }))
172 }
173 Self::Dummy(_) | Self::FallbackAcknowledgement(_) | Self::Terms(_) | Self::OAuth(_) => {
175 Cow::Owned(JsonObject::default())
176 }
177 Self::_Custom(c) => Cow::Borrowed(&c.extra),
178 }
179 }
180}
181
182impl fmt::Debug for AuthData {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 match self {
186 Self::Password(inner) => inner.fmt(f),
187 Self::ReCaptcha(inner) => inner.fmt(f),
188 Self::EmailIdentity(inner) => inner.fmt(f),
189 Self::Msisdn(inner) => inner.fmt(f),
190 Self::Dummy(inner) => inner.fmt(f),
191 Self::RegistrationToken(inner) => inner.fmt(f),
192 Self::FallbackAcknowledgement(inner) => inner.fmt(f),
193 Self::Terms(inner) => inner.fmt(f),
194 Self::OAuth(inner) => inner.fmt(f),
195 Self::_Custom(inner) => inner.fmt(f),
196 }
197 }
198}
199
200#[derive(Clone, Deserialize, Serialize)]
206#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
207#[serde(tag = "type", rename = "m.login.password")]
208pub struct Password {
209 pub identifier: UserIdentifier,
211
212 pub password: String,
214
215 pub session: Option<String>,
217}
218
219impl Password {
220 pub fn new(identifier: UserIdentifier, password: String) -> Self {
222 Self { identifier, password, session: None }
223 }
224}
225
226impl fmt::Debug for Password {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 let Self { identifier, password: _, session } = self;
229 f.debug_struct("Password")
230 .field("identifier", identifier)
231 .field("session", session)
232 .finish_non_exhaustive()
233 }
234}
235
236#[derive(Clone, Deserialize, Serialize)]
242#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
243#[serde(tag = "type", rename = "m.login.recaptcha")]
244pub struct ReCaptcha {
245 pub response: String,
247
248 pub session: Option<String>,
250}
251
252impl ReCaptcha {
253 pub fn new(response: String) -> Self {
255 Self { response, session: None }
256 }
257}
258
259impl fmt::Debug for ReCaptcha {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 let Self { response: _, session } = self;
262 f.debug_struct("ReCaptcha").field("session", session).finish_non_exhaustive()
263 }
264}
265
266#[derive(Clone, Debug, Deserialize, Serialize)]
272#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
273#[serde(tag = "type", rename = "m.login.email.identity")]
274pub struct EmailIdentity {
275 #[serde(rename = "threepid_creds")]
277 pub thirdparty_id_creds: ThirdpartyIdCredentials,
278
279 pub session: Option<String>,
281}
282
283#[derive(Clone, Debug, Deserialize, Serialize)]
289#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
290#[serde(tag = "type", rename = "m.login.msisdn")]
291pub struct Msisdn {
292 #[serde(rename = "threepid_creds")]
294 pub thirdparty_id_creds: ThirdpartyIdCredentials,
295
296 pub session: Option<String>,
298}
299
300#[derive(Clone, Debug, Default, Deserialize, Serialize)]
306#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
307#[serde(tag = "type", rename = "m.login.dummy")]
308pub struct Dummy {
309 pub session: Option<String>,
311}
312
313impl Dummy {
314 pub fn new() -> Self {
316 Self::default()
317 }
318}
319
320#[derive(Clone, Deserialize, Serialize)]
326#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
327#[serde(tag = "type", rename = "m.login.registration_token")]
328pub struct RegistrationToken {
329 pub token: String,
331
332 pub session: Option<String>,
334}
335
336impl RegistrationToken {
337 pub fn new(token: String) -> Self {
339 Self { token, session: None }
340 }
341}
342
343impl fmt::Debug for RegistrationToken {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 let Self { token: _, session } = self;
346 f.debug_struct("RegistrationToken").field("session", session).finish_non_exhaustive()
347 }
348}
349
350#[derive(Clone, Debug, Deserialize, Serialize)]
356#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
357pub struct FallbackAcknowledgement {
358 pub session: String,
360}
361
362impl FallbackAcknowledgement {
363 pub fn new(session: String) -> Self {
365 Self { session }
366 }
367}
368
369#[derive(Clone, Debug, Default, Deserialize, Serialize)]
377#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
378#[serde(tag = "type", rename = "m.login.terms")]
379pub struct Terms {
380 pub session: Option<String>,
382}
383
384impl Terms {
385 pub fn new() -> Self {
387 Self::default()
388 }
389}
390
391#[derive(Clone, Debug, Default, Deserialize, Serialize)]
397#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
398#[serde(tag = "type", rename = "m.oauth")]
399pub struct OAuth {
400 pub session: Option<String>,
402}
403
404impl OAuth {
405 pub fn new() -> Self {
407 Self::default()
408 }
409}
410
411#[doc(hidden)]
413#[derive(Clone, Deserialize, Serialize)]
414#[non_exhaustive]
415pub struct CustomAuthData {
416 #[serde(rename = "type")]
418 auth_type: String,
419
420 session: Option<String>,
422
423 #[serde(flatten)]
425 extra: JsonObject,
426}
427
428impl fmt::Debug for CustomAuthData {
429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 let Self { auth_type, session, extra: _ } = self;
431 f.debug_struct("CustomAuthData")
432 .field("auth_type", auth_type)
433 .field("session", session)
434 .finish_non_exhaustive()
435 }
436}
437
438#[derive(Clone, Debug, PartialEq, Eq)]
440#[allow(clippy::exhaustive_enums)]
441pub enum UserIdentifier {
442 UserIdOrLocalpart(String),
445
446 Email {
448 address: String,
450 },
451
452 Msisdn {
454 number: String,
458 },
459
460 PhoneNumber {
464 country: String,
470
471 phone: String,
473 },
474
475 #[doc(hidden)]
477 _CustomThirdParty(CustomThirdPartyId),
478}
479
480impl UserIdentifier {
481 pub fn third_party_id(medium: Medium, address: String) -> Self {
483 match medium {
484 Medium::Email => Self::Email { address },
485 Medium::Msisdn => Self::Msisdn { number: address },
486 _ => Self::_CustomThirdParty(CustomThirdPartyId { medium, address }),
487 }
488 }
489
490 pub fn as_third_party_id(&self) -> Option<(&Medium, &str)> {
492 match self {
493 Self::Email { address } => Some((&Medium::Email, address)),
494 Self::Msisdn { number } => Some((&Medium::Msisdn, number)),
495 Self::_CustomThirdParty(CustomThirdPartyId { medium, address }) => {
496 Some((medium, address))
497 }
498 _ => None,
499 }
500 }
501}
502
503impl From<OwnedUserId> for UserIdentifier {
504 fn from(id: OwnedUserId) -> Self {
505 Self::UserIdOrLocalpart(id.into())
506 }
507}
508
509impl From<&OwnedUserId> for UserIdentifier {
510 fn from(id: &OwnedUserId) -> Self {
511 Self::UserIdOrLocalpart(id.as_str().to_owned())
512 }
513}
514
515#[doc(hidden)]
517#[derive(Clone, Debug, PartialEq, Eq)]
518#[non_exhaustive]
519pub struct CustomThirdPartyId {
520 medium: Medium,
522
523 address: String,
525}
526
527#[derive(Clone, Deserialize, Serialize)]
529#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
530pub struct ThirdpartyIdCredentials {
531 pub sid: OwnedSessionId,
533
534 pub client_secret: OwnedClientSecret,
536
537 #[serde(skip_serializing_if = "Option::is_none")]
539 pub id_server: Option<String>,
540
541 #[serde(skip_serializing_if = "Option::is_none")]
543 pub id_access_token: Option<String>,
544}
545
546impl ThirdpartyIdCredentials {
547 pub fn new(sid: OwnedSessionId, client_secret: OwnedClientSecret) -> Self {
549 Self { sid, client_secret, id_server: None, id_access_token: None }
550 }
551}
552
553impl fmt::Debug for ThirdpartyIdCredentials {
554 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
555 let Self { sid, client_secret: _, id_server, id_access_token } = self;
556 f.debug_struct("ThirdpartyIdCredentials")
557 .field("sid", sid)
558 .field("id_server", id_server)
559 .field("id_access_token", id_access_token)
560 .finish_non_exhaustive()
561 }
562}