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