better_auth_core/
session.rs1use chrono::Utc;
2use std::sync::Arc;
3
4use crate::adapters::DatabaseAdapter;
5use crate::config::AuthConfig;
6use crate::entity::{AuthSession, AuthUser};
7use crate::error::AuthResult;
8use crate::types::CreateSession;
9
10pub struct SessionManager<DB: DatabaseAdapter> {
12 config: Arc<AuthConfig>,
13 database: Arc<DB>,
14}
15
16impl<DB: DatabaseAdapter> SessionManager<DB> {
17 pub fn new(config: Arc<AuthConfig>, database: Arc<DB>) -> Self {
18 Self { config, database }
19 }
20
21 pub async fn create_session(
23 &self,
24 user: &impl AuthUser,
25 ip_address: Option<String>,
26 user_agent: Option<String>,
27 ) -> AuthResult<DB::Session> {
28 let expires_at = Utc::now() + self.config.session.expires_in;
29
30 let create_session = CreateSession {
31 user_id: user.id().to_string(),
32 expires_at,
33 ip_address,
34 user_agent,
35 impersonated_by: None,
36 active_organization_id: None,
37 };
38
39 let session = self.database.create_session(create_session).await?;
40 Ok(session)
41 }
42
43 pub async fn get_session(&self, token: &str) -> AuthResult<Option<DB::Session>> {
45 let session = self.database.get_session(token).await?;
46
47 if let Some(ref session) = session {
49 if session.expires_at() < Utc::now() || !session.active() {
50 self.database.delete_session(token).await?;
52 return Ok(None);
53 }
54
55 if self.config.session.update_age {
57 let new_expires_at = Utc::now() + self.config.session.expires_in;
58 let _ = self
59 .database
60 .update_session_expiry(token, new_expires_at)
61 .await;
62 }
63 }
64
65 Ok(session)
66 }
67
68 pub async fn delete_session(&self, token: &str) -> AuthResult<()> {
70 self.database.delete_session(token).await?;
71 Ok(())
72 }
73
74 pub async fn delete_user_sessions(&self, user_id: &str) -> AuthResult<()> {
76 self.database.delete_user_sessions(user_id).await?;
77 Ok(())
78 }
79
80 pub async fn list_user_sessions(&self, user_id: &str) -> AuthResult<Vec<DB::Session>> {
82 let sessions = self.database.get_user_sessions(user_id).await?;
83 let now = Utc::now();
84
85 let active_sessions: Vec<DB::Session> = sessions
87 .into_iter()
88 .filter(|session| session.expires_at() > now && session.active())
89 .collect();
90
91 Ok(active_sessions)
92 }
93
94 pub async fn revoke_session(&self, token: &str) -> AuthResult<bool> {
96 let session_exists = self.get_session(token).await?.is_some();
98
99 if session_exists {
100 self.delete_session(token).await?;
101 Ok(true)
102 } else {
103 Ok(false)
104 }
105 }
106
107 pub async fn revoke_all_user_sessions(&self, user_id: &str) -> AuthResult<usize> {
109 let sessions = self.list_user_sessions(user_id).await?;
111 let count = sessions.len();
112
113 self.delete_user_sessions(user_id).await?;
114 Ok(count)
115 }
116
117 pub async fn revoke_other_user_sessions(
119 &self,
120 user_id: &str,
121 current_token: &str,
122 ) -> AuthResult<usize> {
123 let sessions = self.list_user_sessions(user_id).await?;
124 let mut count = 0;
125
126 for session in sessions {
127 if session.token() != current_token {
128 self.delete_session(session.token()).await?;
129 count += 1;
130 }
131 }
132
133 Ok(count)
134 }
135
136 pub async fn cleanup_expired_sessions(&self) -> AuthResult<usize> {
138 let count = self.database.delete_expired_sessions().await?;
139 Ok(count)
140 }
141
142 pub fn validate_token_format(&self, token: &str) -> bool {
144 token.starts_with("session_") && token.len() > 40
145 }
146
147 pub fn extract_session_token(&self, req: &crate::types::AuthRequest) -> Option<String> {
152 if let Some(auth_header) = req.headers.get("authorization")
154 && let Some(token) = auth_header.strip_prefix("Bearer ")
155 {
156 return Some(token.to_string());
157 }
158
159 if let Some(cookie_header) = req.headers.get("cookie") {
161 let cookie_name = &self.config.session.cookie_name;
162 for part in cookie_header.split(';') {
163 let part = part.trim();
164 if let Some(value) = part.strip_prefix(&format!("{}=", cookie_name))
165 && !value.is_empty()
166 {
167 return Some(value.to_string());
168 }
169 }
170 }
171
172 None
173 }
174}