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<()> {
50 Err(AuthError::NotImplemented(
51 "Preserving ended session rows is unsupported".into(),
52 ))
53 }
54 async fn end_user_sessions_preserving(&self, _user_id: &str) -> AuthResult<()> {
56 Err(AuthError::NotImplemented(
57 "Preserving ended session rows is unsupported".into(),
58 ))
59 }
60 async fn get_session_user(&self, _token: &str) -> AuthResult<Option<S::User>> {
63 Ok(None)
64 }
65
66 async fn get_session_user_record(
68 &self,
69 token: &str,
70 ) -> AuthResult<Option<AdapterRecord<S::User>>> {
71 self.get_session_user(token)
72 .await?
73 .map(AdapterRecord::physical)
74 .transpose()
75 }
76
77 async fn create_session_record(
80 &self,
81 create_session: CreateSession,
82 ) -> AuthResult<AdapterRecord<S::Session>> {
83 AdapterRecord::physical(self.create_session(create_session).await?)
84 }
85
86 async fn get_session_record(
89 &self,
90 token: &str,
91 ) -> AuthResult<Option<AdapterRecord<S::Session>>> {
92 self.get_session(token)
93 .await?
94 .map(AdapterRecord::physical)
95 .transpose()
96 }
97
98 async fn get_sessions_by_tokens_record(
101 &self,
102 tokens: &[String],
103 ) -> AuthResult<Vec<AdapterRecord<S::Session>>> {
104 self.get_sessions_by_tokens(tokens)
105 .await?
106 .into_iter()
107 .map(AdapterRecord::physical)
108 .collect()
109 }
110
111 async fn get_user_sessions_record(
114 &self,
115 user_id: &str,
116 ) -> AuthResult<Vec<AdapterRecord<S::Session>>> {
117 self.get_user_sessions(user_id)
118 .await?
119 .into_iter()
120 .map(AdapterRecord::physical)
121 .collect()
122 }
123
124 async fn get_active_user_sessions_record(
127 &self,
128 user_id: &str,
129 ) -> AuthResult<Vec<AdapterRecord<S::Session>>> {
130 let now = chrono::Utc::now();
131 self.get_user_sessions(user_id)
132 .await?
133 .into_iter()
134 .filter(|session| session.expires_at() > now)
135 .map(AdapterRecord::physical)
136 .collect()
137 }
138
139 async fn refresh_session_record(
142 &self,
143 token: &str,
144 expires_at: chrono::DateTime<chrono::Utc>,
145 ) -> AuthResult<Option<AdapterRecord<S::Session>>> {
146 self.refresh_session(token, expires_at)
147 .await?
148 .map(AdapterRecord::physical)
149 .transpose()
150 }
151
152 async fn update_session_fields_record(
155 &self,
156 token: &str,
157 fields: FieldValues,
158 ) -> AuthResult<Option<AdapterRecord<S::Session>>> {
159 self.update_session_fields(token, fields)
160 .await?
161 .map(AdapterRecord::physical)
162 .transpose()
163 }
164
165 async fn update_session_active_organization_record(
168 &self,
169 token: &str,
170 organization_id: Option<&str>,
171 ) -> AuthResult<AdapterRecord<S::Session>> {
172 AdapterRecord::physical(
173 self.update_session_active_organization(token, organization_id)
174 .await?,
175 )
176 }
177
178 async fn update_session_active_team_record(
181 &self,
182 token: &str,
183 team_id: Option<&str>,
184 ) -> AuthResult<AdapterRecord<S::Session>> {
185 AdapterRecord::physical(self.update_session_active_team(token, team_id).await?)
186 }
187
188 async fn update_session_fields(
190 &self,
191 _token: &str,
192 _fields: FieldValues,
193 ) -> AuthResult<Option<S::Session>> {
194 Err(AuthError::internal(
195 "the store does not support session field updates",
196 ))
197 }
198
199 async fn create_session(&self, create_session: CreateSession) -> AuthResult<S::Session>;
200
201 async fn get_session(&self, token: &str) -> AuthResult<Option<S::Session>>;
202 async fn get_session_by_id(&self, _id: &str) -> AuthResult<Option<S::Session>> {
204 Err(AuthError::NotImplemented(
205 "Session ID lookup is unsupported".into(),
206 ))
207 }
208
209 async fn get_sessions_by_tokens(&self, tokens: &[String]) -> AuthResult<Vec<S::Session>> {
212 let mut sessions = Vec::new();
213 for token in BTreeSet::from_iter(tokens) {
214 if let Some(session) = self.get_session(token).await? {
215 sessions.push(session);
216 }
217 }
218 Ok(sessions)
219 }
220
221 async fn get_user_sessions(&self, user_id: &str) -> AuthResult<Vec<S::Session>>;
222
223 async fn update_session_expiry(
224 &self,
225 token: &str,
226 expires_at: chrono::DateTime<chrono::Utc>,
227 ) -> AuthResult<()>;
228
229 async fn refresh_session_with_fields(
234 &self,
235 token: &str,
236 expires_at: chrono::DateTime<chrono::Utc>,
237 mut fields: FieldValues,
238 ) -> AuthResult<Option<S::Session>> {
239 fields.apply_adapter_transforms_async().await?;
240 if !fields.is_empty() {
241 return Err(AuthError::NotImplemented(
242 "The store does not support refresh field updates".into(),
243 ));
244 }
245 self.refresh_session(token, expires_at).await
246 }
247
248 async fn refresh_session(
251 &self,
252 token: &str,
253 expires_at: chrono::DateTime<chrono::Utc>,
254 ) -> AuthResult<Option<S::Session>> {
255 match self.update_session_expiry(token, expires_at).await {
256 Ok(()) => self.get_session(token).await,
257 Err(AuthError::SessionNotFound) => Ok(None),
258 Err(error) => Err(error),
259 }
260 }
261
262 async fn delete_session(&self, token: &str) -> AuthResult<()>;
263
264 async fn delete_user_sessions(&self, user_id: &str) -> AuthResult<()>;
265
266 async fn delete_expired_sessions(&self) -> AuthResult<usize>;
267
268 async fn update_session_active_organization(
269 &self,
270 token: &str,
271 organization_id: Option<&str>,
272 ) -> AuthResult<S::Session>;
273
274 async fn update_session_active_team(
275 &self,
276 _token: &str,
277 _team_id: Option<&str>,
278 ) -> AuthResult<S::Session> {
279 Err(AuthError::internal(
280 "active-team updates are not supported by this store",
281 ))
282 }
283}