1use async_trait::async_trait;
2use authly_core::{AuthError, Identity};
3use serde::{Deserialize, Serialize};
4
5use std::collections::HashMap;
6use std::sync::Mutex;
7
8#[cfg(feature = "store-sqlx")]
9pub mod sql_store;
10
11#[cfg(feature = "store-sqlx")]
12pub use sql_store::{SqlSessionStore, SqlStore};
13
14#[cfg(feature = "store-redis")]
15pub mod redis_store;
16
17#[cfg(feature = "store-redis")]
18pub use redis_store::RedisStore;
19
20#[derive(Clone, Debug, Serialize, Deserialize)]
21pub struct Session {
22 pub id: String,
23 pub identity: Identity,
24 pub expires_at: chrono::DateTime<chrono::Utc>,
25}
26
27#[async_trait]
28pub trait SessionStore: Send + Sync + 'static {
29 async fn load_session(&self, id: &str) -> Result<Option<Session>, AuthError>;
30 async fn save_session(&self, session: &Session) -> Result<(), AuthError>;
31 async fn delete_session(&self, id: &str) -> Result<(), AuthError>;
32}
33
34#[derive(Default)]
35pub struct MemoryStore {
36 sessions: Mutex<HashMap<String, Session>>,
37}
38
39impl MemoryStore {
40 pub fn new() -> Self {
41 Self::default()
42 }
43}
44
45#[async_trait]
46impl SessionStore for MemoryStore {
47 async fn load_session(&self, id: &str) -> Result<Option<Session>, AuthError> {
48 Ok(self.sessions.lock().unwrap().get(id).cloned())
49 }
50 async fn save_session(&self, session: &Session) -> Result<(), AuthError> {
51 self.sessions
52 .lock()
53 .unwrap()
54 .insert(session.id.clone(), session.clone());
55 Ok(())
56 }
57 async fn delete_session(&self, id: &str) -> Result<(), AuthError> {
58 self.sessions.lock().unwrap().remove(id);
59 Ok(())
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[tokio::test]
68 async fn test_memory_store() {
69 let store = MemoryStore::default();
70 let session = Session {
71 id: "test_id".to_string(),
72 identity: Identity {
73 provider_id: "test".to_string(),
74 external_id: "123".to_string(),
75 email: None,
76 username: None,
77 attributes: HashMap::new(),
78 },
79 expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
80 };
81
82 store.save_session(&session).await.unwrap();
83 let loaded = store.load_session("test_id").await.unwrap().unwrap();
84 assert_eq!(loaded.id, "test_id");
85
86 store.delete_session("test_id").await.unwrap();
87 let loaded = store.load_session("test_id").await.unwrap();
88 assert!(loaded.is_none());
89 }
90}