Skip to main content

dittolive_ditto/identity/auth/
authenticator.rs

1use crate::{
2    ditto::{Ditto, DittoFields},
3    error::{DittoError, ErrorKind},
4    utils::prelude::*,
5};
6
7/// Feedback provided by an authentication provider.
8///
9/// This feedback may include arbitrary JSON, and may be used to
10/// give details about an authentication success or explain why it was rejected.
11#[derive(Clone, Debug)]
12pub struct AuthenticationClientFeedback {
13    /// If present, this object contains feedback from the authentication provider.
14    pub feedback: Option<serde_json::Value>,
15}
16
17impl RefCounted for DittoAuthenticator {}
18
19/// Use [`ditto.auth()`] to manage authentication when using applicable identites.
20///
21/// The `DittoAuthenticator` is available when using the [`OnlinePlayground`]
22/// and [`OnlineWithAuthentication`] identities.
23///
24/// [`ditto.auth()`]: crate::Ditto::auth
25#[derive(Clone)]
26pub struct DittoAuthenticator {
27    pub(crate) ditto_fields: std::sync::Weak<DittoFields>,
28}
29
30impl DittoAuthenticator {
31    #[doc(hidden)]
32    #[deprecated(note = "Use `ditto.auth()` instead")]
33    #[cfg(not(test))] // this dangling pattern is error-prone, ensure we don't actually call this.
34    pub fn new() -> Self {
35        DittoAuthenticator {
36            // This is a *dangling* back reference, that used to act as a placeholder.
37            // We don't use this error-prone pattern anymore, this function is just there for the
38            // sake of back-compat (perhaps unnecessarily so).
39            ditto_fields: std::sync::Weak::<DittoFields>::new(),
40        }
41    }
42
43    /// Asks the [`Ditto`] instance to make an auth request to the configured
44    /// [`Identity`]'s auth URL with a single token parameter.
45    ///
46    /// - `token`: An auth or API token you have configured.
47    /// - `provider`: The name of an authentication provider web hook you have configured in Ditto.
48    ///   Cloud, which will accept the `token` and contact your Auth service
49    pub fn login(
50        &self,
51        token: &str,
52        provider: &str,
53    ) -> Result<AuthenticationClientFeedback, DittoError> {
54        let fields = self
55            .ditto_fields
56            .upgrade()
57            .ok_or(ErrorKind::ReleasedDittoInstance)?;
58        let c_token = char_p::new(token);
59        let c_provider = char_p::new(provider);
60        let result = ffi_sdk::ditto_auth_client_login_with_token_and_feedback(
61            &fields.ditto,
62            c_token.as_ref(),
63            c_provider.as_ref(),
64        );
65        fn parse_client_info(c: Option<char_p::Box>) -> AuthenticationClientFeedback {
66            AuthenticationClientFeedback {
67                feedback: c.map(|it| serde_json::from_str(it.to_str()).unwrap()),
68            }
69        }
70
71        match result.status_code {
72            0 => Ok(parse_client_info(result.c_string)),
73            _ => Err(DittoError::from_authentication_feedback(parse_client_info(
74                result.c_string,
75            ))),
76        }
77    }
78
79    #[doc(hidden)]
80    #[deprecated(note = "Use `ditto.auth().login(...)` instead")]
81    pub fn login_with_token_and_feedback(
82        &self,
83        token: &str,
84        provider: &str,
85    ) -> Result<AuthenticationClientFeedback, DittoError> {
86        self.login(token, provider)
87    }
88
89    /// Log out of Ditto.
90    ///
91    /// Shutdown all replication sessions and remove any cached authentication credentials. This
92    /// does *not* remove the local data store.
93    pub fn logout<R>(&self, cleanup: impl FnOnce(Ditto) -> R) -> Result<R, DittoError> {
94        let fields = self
95            .ditto_fields
96            .upgrade()
97            .ok_or(ErrorKind::ReleasedDittoInstance)?;
98        let status = ffi_sdk::ditto_auth_client_logout(&fields.ditto);
99        if status != 0 {
100            return Err(DittoError::from_ffi(ErrorKind::Authentication));
101        }
102        let ditto = Ditto::new_temp(fields);
103        ditto.stop_sync();
104        let ret = cleanup(ditto);
105        Ok(ret)
106    }
107
108    /// Query whether Ditto has a valid authentication token.
109    ///
110    /// This will only be `true` when using an
111    /// [`OnlineWithAuthentication`] identity, after a
112    /// successful login. If the authentication token is allowed to expire then it will return
113    /// `false` instead.
114    pub fn is_authenticated(&self) -> bool {
115        match self.ditto_fields.upgrade() {
116            None => false,
117            Some(fields) => ffi_sdk::ditto_auth_client_is_web_valid(&fields.ditto) != 0,
118        }
119    }
120
121    /// The currently logged-in user ID.
122    ///
123    /// This will return `None` if there is no valid authentication or an
124    /// [`OnlineWithAuthentication`] identity is not being
125    /// used.
126    pub fn user_id(&self) -> Option<String> {
127        let fields = self.ditto_fields.upgrade()?;
128        let c_msg = ffi_sdk::ditto_auth_client_user_id(&fields.ditto)?;
129        Some(c_msg.into_string())
130    }
131}