pub trait SessionStorage: Send + Sync {
// Required methods
fn create_user_session(
&self,
user_id: String,
session_id: Uuid,
expires_at: OffsetDateTime,
) -> impl Future<Output = Result<()>> + Send;
fn get_user_sessions(
&self,
user_id: &str,
) -> impl Future<Output = Result<Vec<SessionData>>> + Send;
fn revoke_user_session(
&self,
user_id: &str,
session_id: &Uuid,
) -> impl Future<Output = Result<()>> + Send;
fn revoke_all_user_sessions(
&self,
user_id: &str,
) -> impl Future<Output = Result<()>> + Send;
// Provided methods
fn user_session_exists(
&self,
user_id: &str,
session_id: &Uuid,
) -> impl Future<Output = Result<bool>> + Send { ... }
fn get_session_data(
&self,
user_id: &str,
session_id: &Uuid,
) -> impl Future<Output = Result<Option<SessionData>>> + Send { ... }
fn cleanup_expired_sessions(
&self,
user_id: &str,
) -> impl Future<Output = Result<()>> + Send { ... }
}Expand description
Trait for session storage backends.
With the JWT-based session data approach, the storage is now responsible for managing refresh tokens organized by user_id. Each user can have multiple active sessions stored as an array. Session data is stored directly in the JWT access token.
All methods are async and return Send futures, allowing them to be used safely across thread boundaries in async contexts.
§Example Implementation
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use time::OffsetDateTime;
use uuid::Uuid;
use axum_jwt_sessions::error::Result;
use axum_jwt_sessions::{SessionData, SessionStorage};
struct UserSessionStorage {
user_sessions: Arc<RwLock<HashMap<String, Vec<SessionData>>>>,
}
impl SessionStorage for UserSessionStorage {
async fn create_user_session(&self, user_id: String, session_id: Uuid, expires_at: OffsetDateTime) -> Result<()> {
let mut sessions = self.user_sessions.write().await;
let user_sessions = sessions.entry(user_id).or_insert_with(Vec::new);
user_sessions.push(SessionData {
session_id,
expires_at,
});
Ok(())
}
async fn get_user_sessions(&self, user_id: &str) -> Result<Vec<SessionData>> {
let sessions = self.user_sessions.read().await;
let now = OffsetDateTime::now_utc();
if let Some(user_sessions) = sessions.get(user_id) {
// Return only non-expired sessions
let active_sessions: Vec<SessionData> = user_sessions
.iter()
.filter(|session| session.expires_at > now)
.cloned()
.collect();
Ok(active_sessions)
} else {
Ok(Vec::new())
}
}
async fn revoke_user_session(&self, user_id: &str, session_id: &Uuid) -> Result<()> {
let mut sessions = self.user_sessions.write().await;
if let Some(user_sessions) = sessions.get_mut(user_id) {
user_sessions.retain(|session| session.session_id != *session_id);
if user_sessions.is_empty() {
sessions.remove(user_id);
}
}
Ok(())
}
async fn revoke_all_user_sessions(&self, user_id: &str) -> Result<()> {
self.user_sessions.write().await.remove(user_id);
Ok(())
}
}Required Methods§
Sourcefn create_user_session(
&self,
user_id: String,
session_id: Uuid,
expires_at: OffsetDateTime,
) -> impl Future<Output = Result<()>> + Send
fn create_user_session( &self, user_id: String, session_id: Uuid, expires_at: OffsetDateTime, ) -> impl Future<Output = Result<()>> + Send
Create a new session for a user
Sourcefn get_user_sessions(
&self,
user_id: &str,
) -> impl Future<Output = Result<Vec<SessionData>>> + Send
fn get_user_sessions( &self, user_id: &str, ) -> impl Future<Output = Result<Vec<SessionData>>> + Send
Get all active sessions for a user (excludes expired sessions)
Provided Methods§
Sourcefn user_session_exists(
&self,
user_id: &str,
session_id: &Uuid,
) -> impl Future<Output = Result<bool>> + Send
fn user_session_exists( &self, user_id: &str, session_id: &Uuid, ) -> impl Future<Output = Result<bool>> + Send
Check if a specific session exists for a user (not revoked or expired)
Sourcefn get_session_data(
&self,
user_id: &str,
session_id: &Uuid,
) -> impl Future<Output = Result<Option<SessionData>>> + Send
fn get_session_data( &self, user_id: &str, session_id: &Uuid, ) -> impl Future<Output = Result<Option<SessionData>>> + Send
Get session data for a specific session_id within a user’s sessions
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.