use crate::auth::error::AuthError;
use crate::auth::state::Identity;
use crate::auth::SameSite;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)]
pub struct SessionConfig {
pub cookie_name: String,
pub secure: bool,
pub http_only: bool,
pub same_site: SameSite,
pub path: String,
pub max_age: Option<chrono::Duration>,
pub state_encryption_key: [u8; 32],
}
impl Default for SessionConfig {
fn default() -> Self {
let mut key = [0u8; 32];
key.copy_from_slice(b"static_key_change_in_production!");
Self {
cookie_name: "authkestra_session".to_string(),
secure: true,
http_only: true,
same_site: SameSite::Lax,
path: "/".to_string(),
max_age: Some(chrono::Duration::hours(24)),
state_encryption_key: key,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Session {
pub id: String,
pub identity: Identity,
pub expires_at: chrono::DateTime<chrono::Utc>,
}
#[async_trait]
pub trait SessionStore: Send + Sync + 'static {
async fn load_session(&self, id: &str) -> Result<Option<Session>, AuthError>;
async fn save_session(&self, session: &Session) -> Result<(), AuthError>;
async fn delete_session(&self, id: &str) -> Result<(), AuthError>;
}
#[async_trait]
impl<S: crate::store::KvStore<Session>> SessionStore for S {
async fn load_session(&self, id: &str) -> Result<Option<Session>, AuthError> {
self.get(id)
.await
.map_err(|e| AuthError::Session(e.to_string()))
}
async fn save_session(&self, session: &Session) -> Result<(), AuthError> {
let ttl_secs = (session.expires_at - chrono::Utc::now()).num_seconds();
let ttl = std::time::Duration::from_secs(if ttl_secs > 0 { ttl_secs as u64 } else { 0 });
self.set(&session.id, session.clone(), ttl)
.await
.map_err(|e| AuthError::Session(e.to_string()))
}
async fn delete_session(&self, id: &str) -> Result<(), AuthError> {
self.delete(id)
.await
.map_err(|e| AuthError::Session(e.to_string()))
}
}