firebase_rs_sdk/app_check/
api.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use crate::app::{get_app, AppError, FirebaseApp};
5
6use super::errors::{AppCheckError, AppCheckResult};
7use super::logger::LOGGER;
8use super::providers::{CustomProvider, ReCaptchaEnterpriseProvider, ReCaptchaV3Provider};
9use super::state;
10use super::types::{
11    AppCheck, AppCheckOptions, AppCheckProvider, AppCheckToken, AppCheckTokenListener,
12    AppCheckTokenResult, ListenerHandle, ListenerType,
13};
14
15pub fn initialize_app_check(
16    app: Option<FirebaseApp>,
17    options: AppCheckOptions,
18) -> AppCheckResult<AppCheck> {
19    let app = if let Some(app) = app {
20        app
21    } else {
22        match get_app(None) {
23            Ok(app) => app,
24            Err(AppError::NoApp { app_name }) => {
25                return Err(AppCheckError::InvalidConfiguration {
26                    message: format!("Firebase app '{app_name}' is not initialized"),
27                });
28            }
29            Err(err) => {
30                return Err(AppCheckError::Internal(err.to_string()));
31            }
32        }
33    };
34
35    let app_check = AppCheck::new(app.clone());
36
37    let provider = options.provider.clone();
38    let auto_refresh = options
39        .is_token_auto_refresh_enabled
40        .unwrap_or_else(|| app.automatic_data_collection_enabled());
41
42    state::ensure_activation(&app_check, provider.clone(), auto_refresh)?;
43
44    provider.initialize(&app);
45
46    if auto_refresh {
47        LOGGER.debug("App Check auto-refresh enabled");
48    }
49
50    Ok(app_check)
51}
52
53pub fn set_token_auto_refresh_enabled(app_check: &AppCheck, enabled: bool) {
54    state::set_auto_refresh(app_check, enabled);
55    if enabled {
56        LOGGER.debug("App Check auto-refresh toggled on");
57    }
58}
59
60pub fn get_token(app_check: &AppCheck, force_refresh: bool) -> AppCheckResult<AppCheckTokenResult> {
61    if !state::is_activated(app_check) {
62        return Err(AppCheckError::UseBeforeActivation {
63            app_name: app_check.app().name().to_owned(),
64        });
65    }
66
67    if !force_refresh {
68        if let Some(token) = state::current_token(app_check) {
69            if !token.is_expired() {
70                return Ok(AppCheckTokenResult::from_token(token));
71            }
72        }
73    }
74
75    let provider =
76        state::provider(app_check).ok_or_else(|| AppCheckError::UseBeforeActivation {
77            app_name: app_check.app().name().to_owned(),
78        })?;
79
80    let token = provider.get_token()?;
81    state::store_token(app_check, token.clone());
82    Ok(AppCheckTokenResult::from_token(token))
83}
84
85pub fn get_limited_use_token(app_check: &AppCheck) -> AppCheckResult<AppCheckTokenResult> {
86    if !state::is_activated(app_check) {
87        return Err(AppCheckError::UseBeforeActivation {
88            app_name: app_check.app().name().to_owned(),
89        });
90    }
91
92    let provider =
93        state::provider(app_check).ok_or_else(|| AppCheckError::UseBeforeActivation {
94            app_name: app_check.app().name().to_owned(),
95        })?;
96
97    let token = provider.get_limited_use_token()?;
98    Ok(AppCheckTokenResult::from_token(token))
99}
100
101pub fn add_token_listener(
102    app_check: &AppCheck,
103    listener: AppCheckTokenListener,
104    listener_type: ListenerType,
105) -> AppCheckResult<ListenerHandle> {
106    if !state::is_activated(app_check) {
107        return Err(AppCheckError::UseBeforeActivation {
108            app_name: app_check.app().name().to_owned(),
109        });
110    }
111
112    let handle = state::add_listener(app_check, listener.clone(), listener_type);
113
114    if let Some(token) = state::current_token(app_check) {
115        listener(&AppCheckTokenResult::from_token(token));
116    }
117
118    Ok(handle)
119}
120
121pub fn remove_token_listener(handle: ListenerHandle) {
122    handle.unsubscribe();
123}
124
125// Helper constructors for simple providers.
126pub fn custom_provider<F>(callback: F) -> Arc<dyn AppCheckProvider>
127where
128    F: Fn() -> AppCheckResult<AppCheckToken> + Send + Sync + 'static,
129{
130    Arc::new(CustomProvider::new(callback))
131}
132
133pub fn recaptcha_v3_provider(site_key: impl Into<String>) -> Arc<dyn AppCheckProvider> {
134    Arc::new(ReCaptchaV3Provider::new(site_key.into()))
135}
136
137pub fn recaptcha_enterprise_provider(site_key: impl Into<String>) -> Arc<dyn AppCheckProvider> {
138    Arc::new(ReCaptchaEnterpriseProvider::new(site_key.into()))
139}
140
141// Convenience helper to build AppCheck tokens for custom providers.
142pub fn token_with_ttl(token: impl Into<String>, ttl: Duration) -> AppCheckResult<AppCheckToken> {
143    AppCheckToken::with_ttl(token, ttl)
144}