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
use crate::{
ditto::{Ditto, DittoFields},
error::{DittoError, ErrorKind},
utils::prelude::*,
};
/// This wraps an optional JSON blob that could be provided by an authentication provider. It can be
/// used to give details about the authentication or explain why it was rejected.
#[derive(Clone, Debug)]
pub struct AuthenticationClientFeedback {
pub feedback: Option<serde_json::Value>,
}
impl RefCounted for DittoAuthenticator {}
#[derive(Clone)]
/// Handle to trigger authentication requests
pub struct DittoAuthenticator {
pub(crate) ditto_fields: std::sync::Weak<DittoFields>,
}
impl DittoAuthenticator {
// TODO(pub_check)
pub fn new() -> Self {
DittoAuthenticator {
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_with_token_and_feedback(
&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,
))),
}
}
/// 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())
}
}