alibi_core/store/contracts/
session.rs1use crate::field_policy::FieldValues;
2use crate::{AdapterRecord, AuthSession};
3use crate::{AuthError, AuthResult, AuthSchema, CreateSession};
4use async_trait::async_trait;
5use std::collections::BTreeSet;
6#[async_trait]
7pub trait SessionStore<S: AuthSchema>: Send + Sync {
8 async fn prepare_secondary_session_creation(
11 &self,
12 _input: CreateSession,
13 _persist: bool,
14 ) -> AuthResult<S::Session> {
15 Err(AuthError::NotImplemented(
16 "Secondary session creation is unsupported".into(),
17 ))
18 }
19 async fn complete_secondary_session_creation(&self, _session: &S::Session) -> AuthResult<()> {
21 Err(AuthError::NotImplemented(
22 "Secondary session creation is unsupported".into(),
23 ))
24 }
25 async fn prepare_secondary_session_update(
27 &self,
28 _session: S::Session,
29 _expires_at: Option<chrono::DateTime<chrono::Utc>>,
30 _fields: FieldValues,
31 ) -> AuthResult<Option<(S::Session, FieldValues)>> {
32 Err(AuthError::NotImplemented(
33 "Secondary session updates are unsupported".into(),
34 ))
35 }
36 async fn complete_secondary_session_update(
38 &self,
39 _session: S::Session,
40 _expires_at: Option<chrono::DateTime<chrono::Utc>>,
41 _fields: FieldValues,
42 _persist: bool,
43 ) -> AuthResult<Option<S::Session>> {
44 Err(AuthError::NotImplemented(
45 "Secondary session updates are unsupported".into(),
46 ))
47 }
48 async fn end_session_preserving(&self, _token: &str) -> AuthResult<bool> {
51 Err(AuthError::NotImplemented(
52 "Preserving ended session rows is unsupported".into(),
53 ))
54 }
55 async fn end_user_sessions_preserving(&self, _user_id: &str) -> AuthResult<bool> {
58 Err(AuthError::NotImplemented(
59 "Preserving ended session rows is unsupported".into(),
60 ))
61 }
62 async fn get_session_user(&self, _token: &str) -> AuthResult<Option<S::User>> {
65 Ok(None)
66 }
67
68 async fn get_session_user_record(
70 &self,
71 token: &str,
72 ) -> AuthResult<Option<AdapterRecord<S::User>>> {
73 self.get_session_user(token)
74 .await?
75 .map(AdapterRecord::physical)
76 .transpose()
77 }
78
79 async fn create_session_record(
82 &self,
83 create_session: CreateSession,
84 ) -> AuthResult<AdapterRecord<S::Session>> {
85 AdapterRecord::physical(self.create_session(create_session).await?)
86 }
87
88 async fn get_session_record(
91 &self,
92 token: &str,
93 ) -> AuthResult<Option<AdapterRecord<S::Session>>> {
94 self.get_session(token)
95 .await?
96 .map(AdapterRecord::physical)
97 .transpose()
98 }
99
100 async fn get_sessions_by_tokens_record(
103 &self,
104 tokens: &[String],
105 ) -> AuthResult<Vec<AdapterRecord<S::Session>>> {
106 self.get_sessions_by_tokens(tokens)
107 .await?
108 .into_iter()
109 .map(AdapterRecord::physical)
110 .collect()
111 }
112
113 async fn get_user_sessions_record(
116 &self,
117 user_id: &str,
118 ) -> AuthResult<Vec<AdapterRecord<S::Session>>> {
119 self.get_user_sessions(user_id)
120 .await?
121 .into_iter()
122 .map(AdapterRecord::physical)
123 .collect()
124 }
125
126 async fn get_active_user_sessions_record(
129 &self,
130 user_id: &str,
131 ) -> AuthResult<Vec<AdapterRecord<S::Session>>> {
132 let now = chrono::Utc::now();
133 self.get_user_sessions(user_id)
134 .await?
135 .into_iter()
136 .filter(|session| session.expires_at() > now)
137 .map(AdapterRecord::physical)
138 .collect()
139 }
140
141 async fn refresh_session_record(
144 &self,
145 token: &str,
146 expires_at: chrono::DateTime<chrono::Utc>,
147 ) -> AuthResult<Option<AdapterRecord<S::Session>>> {
148 self.refresh_session(token, expires_at)
149 .await?
150 .map(AdapterRecord::physical)
151 .transpose()
152 }
153
154 async fn update_session_fields_record(
157 &self,
158 token: &str,
159 fields: FieldValues,
160 ) -> AuthResult<Option<AdapterRecord<S::Session>>> {
161 self.update_session_fields(token, fields)
162 .await?
163 .map(AdapterRecord::physical)
164 .transpose()
165 }
166
167 async fn update_session_active_organization_record(
170 &self,
171 token: &str,
172 organization_id: Option<&str>,
173 ) -> AuthResult<AdapterRecord<S::Session>> {
174 AdapterRecord::physical(
175 self.update_session_active_organization(token, organization_id)
176 .await?,
177 )
178 }
179
180 async fn update_session_active_team_record(
183 &self,
184 token: &str,
185 team_id: Option<&str>,
186 ) -> AuthResult<AdapterRecord<S::Session>> {
187 AdapterRecord::physical(self.update_session_active_team(token, team_id).await?)
188 }
189
190 async fn update_session_fields(
192 &self,
193 _token: &str,
194 _fields: FieldValues,
195 ) -> AuthResult<Option<S::Session>> {
196 Err(AuthError::internal(
197 "the store does not support session field updates",
198 ))
199 }
200
201 async fn create_session(&self, create_session: CreateSession) -> AuthResult<S::Session>;
202
203 async fn get_session(&self, token: &str) -> AuthResult<Option<S::Session>>;
204 async fn get_session_by_id(&self, _id: &str) -> AuthResult<Option<S::Session>> {
206 Err(AuthError::NotImplemented(
207 "Session ID lookup is unsupported".into(),
208 ))
209 }
210
211 async fn get_sessions_by_tokens(&self, tokens: &[String]) -> AuthResult<Vec<S::Session>> {
214 let mut sessions = Vec::new();
215 for token in BTreeSet::from_iter(tokens) {
216 if let Some(session) = self.get_session(token).await? {
217 sessions.push(session);
218 }
219 }
220 Ok(sessions)
221 }
222
223 async fn get_user_sessions(&self, user_id: &str) -> AuthResult<Vec<S::Session>>;
224
225 async fn update_session_expiry(
226 &self,
227 token: &str,
228 expires_at: chrono::DateTime<chrono::Utc>,
229 ) -> AuthResult<()>;
230
231 async fn refresh_session_with_fields(
236 &self,
237 token: &str,
238 expires_at: chrono::DateTime<chrono::Utc>,
239 mut fields: FieldValues,
240 ) -> AuthResult<Option<S::Session>> {
241 fields.apply_adapter_transforms_async().await?;
242 if !fields.is_empty() {
243 return Err(AuthError::NotImplemented(
244 "The store does not support refresh field updates".into(),
245 ));
246 }
247 self.refresh_session(token, expires_at).await
248 }
249
250 async fn refresh_session(
253 &self,
254 token: &str,
255 expires_at: chrono::DateTime<chrono::Utc>,
256 ) -> AuthResult<Option<S::Session>> {
257 match self.update_session_expiry(token, expires_at).await {
258 Ok(()) => self.get_session(token).await,
259 Err(AuthError::SessionNotFound) => Ok(None),
260 Err(error) => Err(error),
261 }
262 }
263
264 async fn delete_session(&self, token: &str) -> AuthResult<()>;
265
266 async fn delete_user_sessions(&self, user_id: &str) -> AuthResult<()>;
267
268 async fn delete_expired_sessions(&self) -> AuthResult<usize>;
269
270 async fn update_session_active_organization(
271 &self,
272 token: &str,
273 organization_id: Option<&str>,
274 ) -> AuthResult<S::Session>;
275
276 async fn update_session_active_team(
277 &self,
278 _token: &str,
279 _team_id: Option<&str>,
280 ) -> AuthResult<S::Session> {
281 Err(AuthError::internal(
282 "active-team updates are not supported by this store",
283 ))
284 }
285}