Skip to main content

ruma_client_api/uiaa/
auth_data.rs

1//! Authentication data types for the different [`AuthType`]s.
2
3use 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/// Information for one authentication stage.
17#[derive(Clone, Serialize)]
18#[non_exhaustive]
19#[serde(untagged)]
20pub enum AuthData {
21    /// Password-based authentication (`m.login.password`).
22    Password(Password),
23
24    /// Google ReCaptcha 2.0 authentication (`m.login.recaptcha`).
25    ReCaptcha(ReCaptcha),
26
27    /// Email-based authentication (`m.login.email.identity`).
28    EmailIdentity(EmailIdentity),
29
30    /// Phone number-based authentication (`m.login.msisdn`).
31    Msisdn(Msisdn),
32
33    /// Dummy authentication (`m.login.dummy`).
34    Dummy(Dummy),
35
36    /// Registration token-based authentication (`m.login.registration_token`).
37    RegistrationToken(RegistrationToken),
38
39    /// Fallback acknowledgement.
40    FallbackAcknowledgement(FallbackAcknowledgement),
41
42    /// Terms of service (`m.login.terms`).
43    ///
44    /// This type is only valid during account registration.
45    Terms(Terms),
46
47    /// OAuth 2.0 (`m.oauth`).
48    ///
49    /// This type is only valid with the cross-signing keys upload endpoint, after logging in with
50    /// the OAuth 2.0 API.
51    OAuth(OAuth),
52
53    /// Unsupported authentication type.
54    #[doc(hidden)]
55    _Custom(CustomAuthData),
56}
57
58impl AuthData {
59    /// Creates a new `AuthData` with the given `auth_type` string, session and data.
60    ///
61    /// Prefer to use the public variants of `AuthData` where possible; this constructor is meant to
62    /// be used for unsupported authentication types only and does not allow setting arbitrary
63    /// data for supported ones.
64    ///
65    /// # Errors
66    ///
67    /// Returns an error if the `auth_type` is known and serialization of `data` to the
68    /// corresponding `AuthData` variant fails.
69    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            _ => Self::_Custom(CustomAuthData { auth_type: auth_type.into(), session, data }),
96        })
97    }
98
99    /// Creates a new `AuthData::FallbackAcknowledgement` with the given session key.
100    pub fn fallback_acknowledgement(session: String) -> Self {
101        Self::FallbackAcknowledgement(FallbackAcknowledgement::new(session))
102    }
103
104    /// Returns the value of the `type` field, if it exists.
105    pub fn auth_type(&self) -> Option<AuthType> {
106        match self {
107            Self::Password(_) => Some(AuthType::Password),
108            Self::ReCaptcha(_) => Some(AuthType::ReCaptcha),
109            Self::EmailIdentity(_) => Some(AuthType::EmailIdentity),
110            Self::Msisdn(_) => Some(AuthType::Msisdn),
111            Self::Dummy(_) => Some(AuthType::Dummy),
112            Self::RegistrationToken(_) => Some(AuthType::RegistrationToken),
113            Self::FallbackAcknowledgement(_) => None,
114            Self::Terms(_) => Some(AuthType::Terms),
115            Self::OAuth(_) => Some(AuthType::OAuth),
116            Self::_Custom(c) => Some(AuthType::_Custom(PrivOwnedStr(c.auth_type.as_str().into()))),
117        }
118    }
119
120    /// Returns the value of the `session` field, if it exists.
121    pub fn session(&self) -> Option<&str> {
122        match self {
123            Self::Password(x) => x.session.as_deref(),
124            Self::ReCaptcha(x) => x.session.as_deref(),
125            Self::EmailIdentity(x) => x.session.as_deref(),
126            Self::Msisdn(x) => x.session.as_deref(),
127            Self::Dummy(x) => x.session.as_deref(),
128            Self::RegistrationToken(x) => x.session.as_deref(),
129            Self::FallbackAcknowledgement(x) => Some(&x.session),
130            Self::Terms(x) => x.session.as_deref(),
131            Self::OAuth(x) => x.session.as_deref(),
132            Self::_Custom(x) => x.session.as_deref(),
133        }
134    }
135
136    /// Returns the associated data.
137    ///
138    /// The returned JSON object won't contain the `type` and `session` fields, use
139    /// [`.auth_type()`][Self::auth_type] / [`.session()`](Self::session) to access those.
140    ///
141    /// Prefer to use the public variants of `AuthData` where possible; this method is meant to be
142    /// used for custom auth types only.
143    pub fn data(&self) -> Cow<'_, JsonObject> {
144        fn serialize<T: Serialize>(obj: T) -> JsonObject {
145            match serde_json::to_value(obj).expect("auth data serialization to succeed") {
146                JsonValue::Object(obj) => obj,
147                _ => panic!("all auth data variants must serialize to objects"),
148            }
149        }
150
151        match self {
152            Self::Password(x) => Cow::Owned(serialize(Password {
153                identifier: x.identifier.clone(),
154                password: x.password.clone(),
155                session: None,
156            })),
157            Self::ReCaptcha(x) => {
158                Cow::Owned(serialize(ReCaptcha { response: x.response.clone(), session: None }))
159            }
160            Self::EmailIdentity(x) => Cow::Owned(serialize(EmailIdentity {
161                thirdparty_id_creds: x.thirdparty_id_creds.clone(),
162                session: None,
163            })),
164            Self::Msisdn(x) => Cow::Owned(serialize(Msisdn {
165                thirdparty_id_creds: x.thirdparty_id_creds.clone(),
166                session: None,
167            })),
168            Self::RegistrationToken(x) => {
169                Cow::Owned(serialize(RegistrationToken { token: x.token.clone(), session: None }))
170            }
171            // These types have no associated data.
172            Self::Dummy(_) | Self::FallbackAcknowledgement(_) | Self::Terms(_) | Self::OAuth(_) => {
173                Cow::Owned(JsonObject::default())
174            }
175            Self::_Custom(c) => Cow::Borrowed(&c.data),
176        }
177    }
178}
179
180impl fmt::Debug for AuthData {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        // Print `Password { .. }` instead of `Password(Password { .. })`
183        match self {
184            Self::Password(inner) => inner.fmt(f),
185            Self::ReCaptcha(inner) => inner.fmt(f),
186            Self::EmailIdentity(inner) => inner.fmt(f),
187            Self::Msisdn(inner) => inner.fmt(f),
188            Self::Dummy(inner) => inner.fmt(f),
189            Self::RegistrationToken(inner) => inner.fmt(f),
190            Self::FallbackAcknowledgement(inner) => inner.fmt(f),
191            Self::Terms(inner) => inner.fmt(f),
192            Self::OAuth(inner) => inner.fmt(f),
193            Self::_Custom(inner) => inner.fmt(f),
194        }
195    }
196}
197
198/// Data for password-based UIAA flow.
199///
200/// See [the spec] for how to use this.
201///
202/// [the spec]: https://spec.matrix.org/v1.18/client-server-api/#password-based
203#[derive(Clone, Deserialize, Serialize)]
204#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
205#[serde(tag = "type", rename = "m.login.password")]
206pub struct Password {
207    /// One of the user's identifiers.
208    pub identifier: UserIdentifier,
209
210    /// The plaintext password.
211    pub password: String,
212
213    /// The value of the session key given by the homeserver, if any.
214    pub session: Option<String>,
215}
216
217impl Password {
218    /// Creates a new `Password` with the given identifier and password.
219    pub fn new(identifier: UserIdentifier, password: String) -> Self {
220        Self { identifier, password, session: None }
221    }
222}
223
224impl fmt::Debug for Password {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        let Self { identifier, password: _, session } = self;
227        f.debug_struct("Password")
228            .field("identifier", identifier)
229            .field("session", session)
230            .finish_non_exhaustive()
231    }
232}
233
234/// Data for ReCaptcha UIAA flow.
235///
236/// See [the spec] for how to use this.
237///
238/// [the spec]: https://spec.matrix.org/v1.18/client-server-api/#google-recaptcha
239#[derive(Clone, Deserialize, Serialize)]
240#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
241#[serde(tag = "type", rename = "m.login.recaptcha")]
242pub struct ReCaptcha {
243    /// The captcha response.
244    pub response: String,
245
246    /// The value of the session key given by the homeserver, if any.
247    pub session: Option<String>,
248}
249
250impl ReCaptcha {
251    /// Creates a new `ReCaptcha` with the given response string.
252    pub fn new(response: String) -> Self {
253        Self { response, session: None }
254    }
255}
256
257impl fmt::Debug for ReCaptcha {
258    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259        let Self { response: _, session } = self;
260        f.debug_struct("ReCaptcha").field("session", session).finish_non_exhaustive()
261    }
262}
263
264/// Data for Email-based UIAA flow.
265///
266/// See [the spec] for how to use this.
267///
268/// [the spec]: https://spec.matrix.org/v1.18/client-server-api/#email-based-identity--homeserver
269#[derive(Clone, Debug, Deserialize, Serialize)]
270#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
271#[serde(tag = "type", rename = "m.login.email.identity")]
272pub struct EmailIdentity {
273    /// Thirdparty identifier credentials.
274    #[serde(rename = "threepid_creds")]
275    pub thirdparty_id_creds: ThirdpartyIdCredentials,
276
277    /// The value of the session key given by the homeserver, if any.
278    pub session: Option<String>,
279}
280
281/// Data for phone number-based UIAA flow.
282///
283/// See [the spec] for how to use this.
284///
285/// [the spec]: https://spec.matrix.org/v1.18/client-server-api/#phone-numbermsisdn-based-identity--homeserver
286#[derive(Clone, Debug, Deserialize, Serialize)]
287#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
288#[serde(tag = "type", rename = "m.login.msisdn")]
289pub struct Msisdn {
290    /// Thirdparty identifier credentials.
291    #[serde(rename = "threepid_creds")]
292    pub thirdparty_id_creds: ThirdpartyIdCredentials,
293
294    /// The value of the session key given by the homeserver, if any.
295    pub session: Option<String>,
296}
297
298/// Data for dummy UIAA flow.
299///
300/// See [the spec] for how to use this.
301///
302/// [the spec]: https://spec.matrix.org/v1.18/client-server-api/#dummy-auth
303#[derive(Clone, Debug, Default, Deserialize, Serialize)]
304#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
305#[serde(tag = "type", rename = "m.login.dummy")]
306pub struct Dummy {
307    /// The value of the session key given by the homeserver, if any.
308    pub session: Option<String>,
309}
310
311impl Dummy {
312    /// Creates an empty `Dummy`.
313    pub fn new() -> Self {
314        Self::default()
315    }
316}
317
318/// Data for registration token-based UIAA flow.
319///
320/// See [the spec] for how to use this.
321///
322/// [the spec]: https://spec.matrix.org/v1.18/client-server-api/#token-authenticated-registration
323#[derive(Clone, Deserialize, Serialize)]
324#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
325#[serde(tag = "type", rename = "m.login.registration_token")]
326pub struct RegistrationToken {
327    /// The registration token.
328    pub token: String,
329
330    /// The value of the session key given by the homeserver, if any.
331    pub session: Option<String>,
332}
333
334impl RegistrationToken {
335    /// Creates a new `RegistrationToken` with the given token.
336    pub fn new(token: String) -> Self {
337        Self { token, session: None }
338    }
339}
340
341impl fmt::Debug for RegistrationToken {
342    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343        let Self { token: _, session } = self;
344        f.debug_struct("RegistrationToken").field("session", session).finish_non_exhaustive()
345    }
346}
347
348/// Data for UIAA fallback acknowledgement.
349///
350/// See [the spec] for how to use this.
351///
352/// [the spec]: https://spec.matrix.org/v1.18/client-server-api/#fallback
353#[derive(Clone, Debug, Deserialize, Serialize)]
354#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
355pub struct FallbackAcknowledgement {
356    /// The value of the session key given by the homeserver.
357    pub session: String,
358}
359
360impl FallbackAcknowledgement {
361    /// Creates a new `FallbackAcknowledgement` with the given session key.
362    pub fn new(session: String) -> Self {
363        Self { session }
364    }
365}
366
367/// Data for terms of service flow.
368///
369/// This type is only valid during account registration.
370///
371/// See [the spec] for how to use this.
372///
373/// [the spec]: https://spec.matrix.org/v1.18/client-server-api/#terms-of-service-at-registration
374#[derive(Clone, Debug, Default, Deserialize, Serialize)]
375#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
376#[serde(tag = "type", rename = "m.login.terms")]
377pub struct Terms {
378    /// The value of the session key given by the homeserver, if any.
379    pub session: Option<String>,
380}
381
382impl Terms {
383    /// Creates an empty `Terms`.
384    pub fn new() -> Self {
385        Self::default()
386    }
387}
388
389/// Data for an [OAuth 2.0-based] UIAA flow.
390///
391/// [OAuth 2.0-based]: https://spec.matrix.org/v1.18/client-server-api/#oauth-authentication
392#[derive(Clone, Debug, Default, Deserialize, Serialize)]
393#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
394#[serde(tag = "type", rename = "m.oauth")]
395pub struct OAuth {
396    /// The value of the session key given by the homeserver, if any.
397    pub session: Option<String>,
398}
399
400impl OAuth {
401    /// Construct an empty `OAuth`.
402    pub fn new() -> Self {
403        Self::default()
404    }
405}
406
407/// Data for an unsupported authentication type.
408#[doc(hidden)]
409#[derive(Clone, Serialize)]
410#[non_exhaustive]
411pub struct CustomAuthData {
412    /// The type of authentication.
413    #[serde(rename = "type")]
414    auth_type: String,
415
416    /// The value of the session key given by the homeserver, if any.
417    session: Option<String>,
418
419    /// Extra data.
420    #[serde(flatten)]
421    data: JsonObject,
422}
423
424impl fmt::Debug for CustomAuthData {
425    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426        let Self { auth_type, session, data: _ } = self;
427        f.debug_struct("CustomAuthData")
428            .field("auth_type", auth_type)
429            .field("session", session)
430            .finish_non_exhaustive()
431    }
432}
433
434/// Identification information for the user.
435#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
436#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
437#[serde(untagged)]
438pub enum UserIdentifier {
439    /// A Matrix user identifier.
440    Matrix(MatrixUserIdentifier),
441
442    /// An email address.
443    Email(EmailUserIdentifier),
444
445    /// A phone number in the MSISDN format.
446    Msisdn(MsisdnUserIdentifier),
447
448    /// A phone number as a separate country code and phone number.
449    PhoneNumber(PhoneNumberUserIdentifier),
450
451    /// Unsupported `m.id.thirdpartyid`.
452    #[doc(hidden)]
453    _CustomThirdParty(CustomThirdPartyUserIdentifier),
454
455    /// Custom identifier type.
456    #[doc(hidden)]
457    _Custom(CustomUserIdentifier),
458}
459
460impl UserIdentifier {
461    /// Returns a reference to the `type` string.
462    pub fn identifier_type(&self) -> &str {
463        match self {
464            Self::Matrix(_) => "m.id.user",
465            Self::Email(_) => "m.id.thirdparty",
466            Self::Msisdn(_) => "m.id.thirdparty",
467            Self::PhoneNumber(_) => "m.id.phone",
468            Self::_CustomThirdParty(_) => "m.id.thirdparty",
469            Self::_Custom(CustomUserIdentifier { identifier_type, .. }) => identifier_type,
470        }
471    }
472
473    /// Creates a new `UserIdentifier` from the given third party identifier.
474    pub fn third_party_id(medium: Medium, address: String) -> Self {
475        match medium {
476            Medium::Email => Self::Email(EmailUserIdentifier { address }),
477            Medium::Msisdn => Self::Msisdn(MsisdnUserIdentifier { number: address }),
478            _ => Self::_CustomThirdParty(CustomThirdPartyUserIdentifier { medium, address }),
479        }
480    }
481
482    /// Get this `UserIdentifier` as a third party identifier if it is one.
483    pub fn as_third_party_id(&self) -> Option<(&Medium, &str)> {
484        match self {
485            Self::Email(EmailUserIdentifier { address }) => Some((&Medium::Email, address)),
486            Self::Msisdn(MsisdnUserIdentifier { number }) => Some((&Medium::Msisdn, number)),
487            Self::_CustomThirdParty(CustomThirdPartyUserIdentifier { medium, address }) => {
488                Some((medium, address))
489            }
490            _ => None,
491        }
492    }
493
494    /// Returns the associated data of the identifier.
495    ///
496    /// The returned JSON object won't contain the `type` field, use
497    /// [`.identifier_type()`][Self::identifier_type] to access it.
498    ///
499    /// Prefer to use the public variants of `UserIdentifier` where possible; this method is meant
500    /// to be used for custom identifier types only.
501    pub fn data(&self) -> Cow<'_, JsonObject> {
502        fn serialize<T: Serialize>(obj: &T) -> JsonObject {
503            match serde_json::to_value(obj).expect("user identifier serialization to succeed") {
504                JsonValue::Object(mut obj) => {
505                    obj.remove("type");
506                    obj
507                }
508                _ => panic!("all user identifiers must serialize to objects"),
509            }
510        }
511
512        match self {
513            Self::Matrix(i) => Cow::Owned(serialize(i)),
514            Self::Email(i) => Cow::Owned(serialize(i)),
515            Self::Msisdn(i) => Cow::Owned(serialize(i)),
516            Self::PhoneNumber(i) => Cow::Owned(serialize(i)),
517            Self::_CustomThirdParty(i) => Cow::Owned(serialize(i)),
518            Self::_Custom(i) => Cow::Borrowed(&i.data),
519        }
520    }
521}
522
523impl From<OwnedUserId> for UserIdentifier {
524    fn from(id: OwnedUserId) -> Self {
525        Self::Matrix(id.into())
526    }
527}
528
529impl From<&OwnedUserId> for UserIdentifier {
530    fn from(id: &OwnedUserId) -> Self {
531        Self::Matrix(id.into())
532    }
533}
534
535impl From<MatrixUserIdentifier> for UserIdentifier {
536    fn from(id: MatrixUserIdentifier) -> Self {
537        Self::Matrix(id)
538    }
539}
540
541impl From<EmailUserIdentifier> for UserIdentifier {
542    fn from(id: EmailUserIdentifier) -> Self {
543        Self::Email(id)
544    }
545}
546
547impl From<MsisdnUserIdentifier> for UserIdentifier {
548    fn from(id: MsisdnUserIdentifier) -> Self {
549        Self::Msisdn(id)
550    }
551}
552
553impl From<PhoneNumberUserIdentifier> for UserIdentifier {
554    fn from(id: PhoneNumberUserIdentifier) -> Self {
555        Self::PhoneNumber(id)
556    }
557}
558
559/// Data for a Matrix user identifier.
560#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
561#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
562#[serde(tag = "type", rename = "m.id.user")]
563pub struct MatrixUserIdentifier {
564    /// Either a fully qualified Matrix user ID, or just the localpart.
565    pub user: String,
566}
567
568impl MatrixUserIdentifier {
569    /// Construct a new `MatrixUserIdentifier` with the given user ID or localpart.
570    pub fn new(user: String) -> Self {
571        Self { user }
572    }
573}
574
575impl From<OwnedUserId> for MatrixUserIdentifier {
576    fn from(id: OwnedUserId) -> Self {
577        Self::new(id.into())
578    }
579}
580
581impl From<&OwnedUserId> for MatrixUserIdentifier {
582    fn from(id: &OwnedUserId) -> Self {
583        Self::new(id.as_str().to_owned())
584    }
585}
586
587/// Data for a email third-party identifier.
588#[derive(Clone, Debug, PartialEq, Eq)]
589#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
590pub struct EmailUserIdentifier {
591    /// The email address.
592    pub address: String,
593}
594
595impl EmailUserIdentifier {
596    /// Construct a new `EmailUserIdentifier` with the given email address.
597    pub fn new(address: String) -> Self {
598        Self { address }
599    }
600}
601
602/// Data for a phone number third-party identifier in the MSISDN format.
603#[derive(Clone, Debug, PartialEq, Eq)]
604#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
605pub struct MsisdnUserIdentifier {
606    /// The phone number according to the [E.164] numbering plan.
607    ///
608    /// [E.164]: https://www.itu.int/rec/T-REC-E.164-201011-I/en
609    pub number: String,
610}
611
612impl MsisdnUserIdentifier {
613    /// Construct a new `MsisdnUserIdentifier` with the given phone number.
614    pub fn new(number: String) -> Self {
615        Self { number }
616    }
617}
618
619/// Data for a phone number identifier as a separate country code and phone number.
620///
621/// The homeserver is responsible for canonicalizing this to the MSISDN format.
622#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
623#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
624#[serde(tag = "type", rename = "m.id.phone")]
625pub struct PhoneNumberUserIdentifier {
626    /// The country that the phone number is from.
627    ///
628    /// This is a two-letter uppercase [ISO-3166-1 alpha-2] country code.
629    ///
630    /// [ISO-3166-1 alpha-2]: https://www.iso.org/iso-3166-country-codes.html
631    pub country: String,
632
633    /// The phone number.
634    pub phone: String,
635}
636
637impl PhoneNumberUserIdentifier {
638    /// Construct a new `PhoneNumberUserIdentifier` with the given country code and phone number.
639    pub fn new(country: String, phone: String) -> Self {
640        Self { country, phone }
641    }
642}
643
644/// Data for an unsupported third-party ID.
645#[doc(hidden)]
646#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
647#[serde(tag = "type", rename = "m.id.thirdparty")]
648pub struct CustomThirdPartyUserIdentifier {
649    /// The kind of the third-party ID.
650    medium: Medium,
651
652    /// The third-party ID.
653    address: String,
654}
655
656/// Data for a custom identifier type.
657#[doc(hidden)]
658#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
659pub struct CustomUserIdentifier {
660    /// The type of the identifier.
661    #[serde(rename = "type")]
662    identifier_type: String,
663
664    /// The data of the identifier.
665    #[serde(flatten)]
666    data: JsonObject,
667}
668
669/// Credentials for third-party authentication (e.g. email / phone number).
670#[derive(Clone, Deserialize, Serialize)]
671#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
672pub struct ThirdpartyIdCredentials {
673    /// Identity server (or homeserver) session ID.
674    pub sid: OwnedSessionId,
675
676    /// Identity server (or homeserver) client secret.
677    pub client_secret: OwnedClientSecret,
678
679    /// Identity server URL.
680    #[serde(skip_serializing_if = "Option::is_none")]
681    pub id_server: Option<String>,
682
683    /// Identity server access token.
684    #[serde(skip_serializing_if = "Option::is_none")]
685    pub id_access_token: Option<String>,
686}
687
688impl ThirdpartyIdCredentials {
689    /// Creates a new `ThirdpartyIdCredentials` with the given session ID and client secret.
690    pub fn new(sid: OwnedSessionId, client_secret: OwnedClientSecret) -> Self {
691        Self { sid, client_secret, id_server: None, id_access_token: None }
692    }
693}
694
695impl fmt::Debug for ThirdpartyIdCredentials {
696    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
697        let Self { sid, client_secret: _, id_server, id_access_token } = self;
698        f.debug_struct("ThirdpartyIdCredentials")
699            .field("sid", sid)
700            .field("id_server", id_server)
701            .field("id_access_token", id_access_token)
702            .finish_non_exhaustive()
703    }
704}