Skip to main content

breez_sdk_spark/session_manager/
mod.rs

1//! User-facing [`SessionManager`] surface for the Breez SDK.
2//!
3//! UniFFI-generated bindings can only export traits defined inside the crate
4//! they're generated from, so we re-declare the trait + supporting types here
5//! and bridge to [`spark_wallet`] via an internal adapter when the SDK is
6//! built. Integrators implement *this* trait — typically backed by a shared
7//! database — to let multiple SDK pods share authentication state.
8//!
9//! Internal layering applied automatically by `SdkBuilder::build()`:
10//!
11//! ```text
12//! auth providers (SO / SSP)
13//!     │ plaintext
14//!     ▼
15//! CachingSessionManager   ← in-memory hot path
16//!     │ plaintext
17//!     ▼
18//! EncryptingSessionManager ← ECIES on Session::token
19//!     │ ciphertext (base64)
20//!     ▼
21//! SessionManagerAdapter | PostgresSessionManager | MysqlSessionManager
22//! | InMemorySessionManager
23//! ```
24
25mod adapter;
26mod caching;
27mod encrypting;
28
29use bitcoin::secp256k1::PublicKey;
30use thiserror::Error;
31
32pub(crate) use adapter::SessionManagerAdapter;
33pub(crate) use caching::CachingSessionManager;
34pub(crate) use encrypting::EncryptingSessionManager;
35
36#[cfg(feature = "uniffi")]
37uniffi::custom_type!(PublicKey, String, {
38    remote,
39    try_lift: |val| {
40        use std::str::FromStr;
41        PublicKey::from_str(&val).map_err(uniffi::deps::anyhow::Error::msg)
42    },
43    lower: |obj| obj.to_string(),
44});
45
46#[derive(Debug, Error, Clone)]
47#[cfg_attr(feature = "uniffi", derive(uniffi::Error))]
48pub enum SessionManagerError {
49    #[error("Session not found")]
50    NotFound,
51    #[error("Generic error: {0}")]
52    Generic(String),
53}
54
55impl From<spark_wallet::SessionManagerError> for SessionManagerError {
56    fn from(e: spark_wallet::SessionManagerError) -> Self {
57        match e {
58            spark_wallet::SessionManagerError::NotFound => SessionManagerError::NotFound,
59            spark_wallet::SessionManagerError::Generic(msg) => SessionManagerError::Generic(msg),
60        }
61    }
62}
63
64impl From<SessionManagerError> for spark_wallet::SessionManagerError {
65    fn from(e: SessionManagerError) -> Self {
66        match e {
67            SessionManagerError::NotFound => spark_wallet::SessionManagerError::NotFound,
68            SessionManagerError::Generic(msg) => spark_wallet::SessionManagerError::Generic(msg),
69        }
70    }
71}
72
73/// Cached authentication session for a single backend service identity.
74#[derive(Clone)]
75#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
76pub struct Session {
77    pub token: String,
78    pub expiration: u64,
79}
80
81impl From<spark_wallet::Session> for Session {
82    fn from(s: spark_wallet::Session) -> Self {
83        Self {
84            token: s.token,
85            expiration: s.expiration,
86        }
87    }
88}
89
90impl From<Session> for spark_wallet::Session {
91    fn from(s: Session) -> Self {
92        Self {
93            token: s.token,
94            expiration: s.expiration,
95        }
96    }
97}
98
99/// Persistent storage for authentication sessions, keyed by the service's
100/// identity public key. Implementations should be thread-safe and may be
101/// backed by an in-memory map (default) or a shared database for cross-pod
102/// auth sharing.
103#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
104#[macros::async_trait]
105pub trait SessionManager: Send + Sync {
106    async fn get_session(
107        &self,
108        service_identity_key: PublicKey,
109    ) -> Result<Session, SessionManagerError>;
110
111    async fn set_session(
112        &self,
113        service_identity_key: PublicKey,
114        session: Session,
115    ) -> Result<(), SessionManagerError>;
116}