dittolive_ditto/identity/auth/
login_provider.rs

1use std::{
2    os::raw::{c_uint, c_void},
3    sync::Arc,
4    time::Duration,
5};
6
7use extern_c::extern_c;
8use ffi_sdk::{ffi_utils::Blocking, BoxedLoginProvider};
9
10use crate::{identity::DittoAuthenticator, utils::prelude::*};
11
12/// Implement this trait for a type in order to construct an
13/// [`OnlineWithAuthentication`] identity.
14// You can find an example in tests::common::mod
15pub trait DittoAuthenticationEventHandler: Send + Sync {
16    /// This will be called when you need to authenticate.
17    /// Usually it will involve a call to `auth.login_with_token`
18    fn authentication_required(&self, auth: DittoAuthenticator);
19
20    /// Allows for custom behavior hooks when authentication is expiring
21    fn authentication_expiring_soon(&self, auth: DittoAuthenticator, seconds_remaining: Duration);
22}
23
24pub(crate) struct LoginProvider(pub(crate) BoxedLoginProvider);
25
26// this inner type allows us to get a reference/ptr before the outer type is
27// constructed
28pub(crate) struct LoginProviderCtx {
29    // The user-provided auth logic (the `.authentication_…()` callbacks above).
30    // Also called an `auth_delegate` in other SDKs.
31    auth_event_handler: Arc<dyn DittoAuthenticationEventHandler + 'static>,
32    // A fancy wrapper around a back-reference to Ditto.
33    authenticator: DittoAuthenticator,
34}
35
36impl LoginProvider {
37    pub fn new(
38        auth_event_handler: Arc<dyn 'static + DittoAuthenticationEventHandler>,
39        authenticator: DittoAuthenticator,
40    ) -> Self {
41        let login_provider_ctx = Arc::new(LoginProviderCtx {
42            auth_event_handler,
43            authenticator,
44        });
45
46        let c_provider = unsafe {
47            ffi_sdk::ditto_auth_client_make_login_provider(
48                // legacy non-preincremented convention.
49                Arc::as_ptr(&login_provider_ctx) as *mut c_void,
50                Some(extern_c(|ctx: *mut c_void, _: Blocking| {
51                    Arc::<LoginProviderCtx>::increment_strong_count(ctx.cast())
52                })),
53                Some(extern_c(|ctx: *mut c_void, _: Blocking| {
54                    Arc::<LoginProviderCtx>::decrement_strong_count(ctx.cast())
55                })),
56                extern_c(|ctx: *mut c_void, secs_remaining: c_uint, _: Blocking| {
57                    let LoginProviderCtx {
58                        auth_event_handler,
59                        authenticator,
60                    }: &LoginProviderCtx = &*ctx.cast();
61                    let authenticator = authenticator.retain();
62                    if let Some(duration) =
63                        (secs_remaining != 0).then(|| Duration::from_secs(secs_remaining.into()))
64                    {
65                        auth_event_handler.authentication_expiring_soon(authenticator, duration);
66                    } else {
67                        auth_event_handler.authentication_required(authenticator);
68                    }
69                }),
70            )
71        };
72        LoginProvider(c_provider)
73    }
74}