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