1use crate::plugin::MetadataMap;
2use crate::session::SessionRequest;
3use crate::wire::{InvitationView, SessionView, UserView};
4use crate::{
5 AdapterOutput, AdapterRecord, AuthAccount, AuthConfig, AuthError, AuthInvitation, AuthRequest,
6 AuthResult, AuthSchema, AuthSession, AuthStore, AuthUser, AuthenticatedUser, ContextExtensions,
7 EmailProvider, SessionManager, VerificationEmailOverrideHandle,
8};
9use std::collections::BTreeMap;
10use std::sync::Arc;
11
12pub struct AuthContext<S: AuthSchema> {
14 pub config: Arc<AuthConfig>,
15 pub database: Arc<dyn AuthStore<S>>,
16 pub email_provider: Option<Arc<dyn EmailProvider>>,
17 pub metadata: MetadataMap,
18 pub extensions: ContextExtensions,
19}
20
21struct AuthoritativeSessionInput<'a, R> {
24 original: &'a R,
25 logical: Option<crate::endpoint::EndpointCall>,
26 stateful: bool,
27}
28impl<R: SessionRequest> SessionRequest for AuthoritativeSessionInput<'_, R> {
29 fn session_headers(&self) -> &std::collections::HashMap<String, String> {
32 self.original.session_headers()
33 }
34 fn session_method(&self) -> &crate::HttpMethod {
35 self.logical.as_ref().map_or_else(
36 || self.original.session_method(),
37 SessionRequest::session_method,
38 )
39 }
40 fn session_query_truthy(&self, name: &str) -> bool {
41 (self.stateful && name == "disableCookieCache")
42 || self.logical.as_ref().map_or_else(
43 || self.original.session_query_truthy(name),
44 |call| call.session_query_truthy(name),
45 )
46 }
47 fn extensions(&self) -> &crate::types::RequestExtensions {
48 self.original.extensions()
49 }
50 fn virtual_session<S: AuthSchema>(&self, _: &AuthContext<S>) -> Option<SessionView> {
51 None
52 }
53 fn take_response_headers(&self) -> crate::Headers {
54 self.original.take_response_headers()
55 }
56 fn queue_response_header(&self, name: impl Into<String>, value: impl Into<String>) {
57 self.original.queue_response_header(name, value);
58 }
59 fn set_session_hook_snapshot(&self, user: UserView, session: SessionView) {
60 self.original.set_session_hook_snapshot(user, session);
61 }
62}
63
64fn insert_null(fields: &mut BTreeMap<String, serde_json::Value>, name: &str) {
65 _ = fields.insert(name.to_owned(), serde_json::Value::Null);
66}
67
68impl<S: AuthSchema> Clone for AuthContext<S> {
69 fn clone(&self) -> Self {
70 Self {
71 config: Arc::clone(&self.config),
72 database: Arc::clone(&self.database),
73 email_provider: self.email_provider.clone(),
74 metadata: self.metadata.clone(),
75 extensions: self.extensions.clone(),
76 }
77 }
78}
79
80impl<S: AuthSchema> AuthContext<S> {
81 pub fn parse_user_fields(
84 &self,
85 input: &indexmap::IndexMap<String, crate::utils::json::JsValue>,
86 creation: bool,
87 ) -> Result<crate::field_policy::FieldValues, crate::field_policy::FieldInputError> {
88 let registered = self.extensions.get::<crate::field_policy::UserFields>();
89 let configured =
90 crate::field_policy::SessionFields(self.config.user.additional_fields.clone());
91 let fields = registered.as_ref().map_or(&configured, |fields| &fields.0);
92 if creation {
93 fields.parse_create(input)
94 } else {
95 fields.parse_update(input)
96 }
97 }
98
99 pub async fn hash_password(
104 &self,
105 hasher: Option<&Arc<dyn crate::utils::password::PasswordHasher>>,
106 password: &str,
107 ) -> AuthResult<String> {
108 let context = crate::endpoint::current_endpoint_call_context().map_or_else(
109 || {
110 crate::hooks::current_request_hook_context()
111 .map(crate::utils::password::PasswordHashContext::from_request)
112 },
113 |call| {
114 Some(crate::utils::password::PasswordHashContext {
115 path: call.path().map(str::to_owned),
116 request: call
117 .request()
118 .map(crate::hooks::RequestHookContext::from_request),
119 })
120 },
121 );
122 self.hash_password_with_context(hasher, password, context.as_ref())
123 .await
124 }
125
126 pub async fn hash_password_with_context(
132 &self,
133 hasher: Option<&Arc<dyn crate::utils::password::PasswordHasher>>,
134 password: &str,
135 context: Option<&crate::utils::password::PasswordHashContext>,
136 ) -> AuthResult<String> {
137 if let Some(hooks) = self
138 .extensions
139 .get::<crate::utils::password::PasswordHashHooks>()
140 {
141 for hook in hooks.0.iter().rev() {
142 hook.before_hash(password, context).await?;
143 }
144 }
145 crate::utils::password::hash_password(hasher, password).await
146 }
147
148 #[must_use]
149 pub fn new(config: Arc<AuthConfig>, database: Arc<dyn AuthStore<S>>) -> Self {
150 Self::with_metadata(config, database, MetadataMap::new())
151 }
152
153 #[must_use]
154 pub fn with_metadata(
155 config: Arc<AuthConfig>,
156 database: Arc<dyn AuthStore<S>>,
157 metadata: MetadataMap,
158 ) -> Self {
159 let config = if config.account.store_state_strategy == crate::OAuthStateStrategy::Automatic
160 {
161 let mut initialized = (*config).clone();
162 initialized.account.store_state_strategy = crate::OAuthStateStrategy::Database;
164 Arc::new(initialized)
165 } else {
166 config
167 };
168 let email_provider = config.email_provider.clone();
169 Self {
170 config,
171 database,
172 email_provider,
173 metadata,
174 extensions: ContextExtensions::default(),
175 }
176 }
177
178 pub fn set_metadata(&mut self, key: impl Into<String>, value: serde_json::Value) {
179 _ = self.metadata.insert(key.into(), value);
180 }
181
182 #[must_use]
183 pub fn get_metadata(&self, key: &str) -> Option<&serde_json::Value> {
184 self.metadata.get(key)
185 }
186
187 #[must_use]
188 pub fn with_extensions(mut self, extensions: ContextExtensions) -> Self {
189 self.extensions = extensions;
190 self
191 }
192
193 #[must_use]
194 pub fn email_verification_override(&self) -> Option<Arc<VerificationEmailOverrideHandle<S>>> {
195 self.extensions.get()
196 }
197
198 pub fn user_view(&self, user: &impl AuthUser) -> UserView {
199 self.project_user_view(user, true)
200 }
201
202 pub fn trusted_user_view(&self, user: &impl AuthUser) -> UserView {
205 self.project_user_view(user, false)
206 }
207
208 pub(in crate::plugin) fn project_user_view(
209 &self,
210 user: &impl AuthUser,
211 public: bool,
212 ) -> UserView {
213 if let Some(view) = user.retained_user_view() {
214 let mut view = view.clone();
215 if public {
216 let registered = self.extensions.get::<crate::field_policy::UserFields>();
217 let fields = registered
218 .as_ref()
219 .map_or(&self.config.user.additional_fields, |fields| &fields.0.0);
220 view.extension_fields
221 .retain(|name, _| fields.get(name).is_none_or(|field| field.returned));
222 }
223 return view;
224 }
225 let mut view = UserView::from(user);
226 if self.feature_enabled("username.enabled") {
227 for (key, absent) in [
228 ("username", view.username.is_none()),
229 ("displayUsername", view.display_username.is_none()),
230 ] {
231 if absent {
232 insert_null(&mut view.extension_fields, key);
233 }
234 }
235 } else {
236 view.username = None;
237 view.display_username = None;
238 }
239 if self.feature_enabled("two_factor.enabled") {
240 view.two_factor_enabled = user.two_factor_enabled_value();
241 if view.two_factor_enabled.is_none() {
242 insert_null(&mut view.extension_fields, "twoFactorEnabled");
243 }
244 } else {
245 view.two_factor_enabled = None;
246 }
247 if self.feature_enabled("admin.enabled") {
248 view.banned = user.banned_value();
249 for (key, absent) in [
250 ("role", view.role.is_none()),
251 ("banned", view.banned.is_none()),
252 ("banReason", view.ban_reason.is_none()),
253 ("banExpires", view.ban_expires.is_none()),
254 ] {
255 if absent {
256 insert_null(&mut view.extension_fields, key);
257 }
258 }
259 } else {
260 view.role = None;
261 view.banned = None;
262 view.ban_reason = None;
263 view.ban_expires = None;
264 }
265
266 if self.feature_enabled("anonymous.enabled") {
267 view.is_anonymous = Some(user.is_anonymous().unwrap_or(false));
268 } else {
269 view.is_anonymous = None;
270 }
271 if self.feature_enabled("phone-number.enabled") {
272 if view.phone_number.is_none() {
273 insert_null(&mut view.extension_fields, "phoneNumber");
274 }
275 if view.phone_number_verified.is_none() {
276 insert_null(&mut view.extension_fields, "phoneNumberVerified");
277 }
278 } else {
279 view.phone_number = None;
280 view.phone_number_verified = None;
281 }
282 if self.feature_enabled("last-login-method.enabled") {
283 if view.last_login_method.is_none() {
284 insert_null(&mut view.extension_fields, "lastLoginMethod");
285 }
286 } else {
287 view.last_login_method = None;
288 }
289 let registered = self.extensions.get::<crate::field_policy::UserFields>();
290 let fields = registered
291 .as_ref()
292 .map_or(&self.config.user.additional_fields, |fields| &fields.0.0);
293 let physical = user.additional_fields();
294 let values = user
295 .adapter_snapshot()
296 .map_or(&physical, AdapterOutput::values);
297 for (name, field) in fields {
298 _ = view.extension_fields.remove(name);
299 if (!public || field.returned)
300 && let Some(value) = values.get(name).or_else(|| {
301 field
302 .field_name
303 .as_ref()
304 .and_then(|physical| values.get(physical))
305 })
306 {
307 _ = view.extension_fields.insert(name.clone(), value.clone());
308 }
309 }
310 if let Some(snapshot) = user.adapter_snapshot() {
311 for name in [
314 "id",
315 "name",
316 "email",
317 "emailVerified",
318 "image",
319 "createdAt",
320 "updatedAt",
321 "username",
322 "displayUsername",
323 "twoFactorEnabled",
324 "role",
325 "banned",
326 "banReason",
327 "banExpires",
328 "isAnonymous",
329 "phoneNumber",
330 "phoneNumberVerified",
331 "lastLoginMethod",
332 ] {
333 if !snapshot.contains_field(name) || snapshot.field_is_undefined(name) {
334 _ = view.omitted_fields.insert(name.into());
335 }
336 }
337 }
338 if self.config.session.stateless {
339 for (name, absent) in [
340 ("image", view.image.is_none()),
341 ("username", view.username.is_none()),
342 ("displayUsername", view.display_username.is_none()),
343 ("banReason", view.ban_reason.is_none()),
344 ("banExpires", view.ban_expires.is_none()),
345 ] {
346 if absent
347 && view
348 .extension_fields
349 .get(name)
350 .is_none_or(serde_json::Value::is_null)
351 {
352 _ = view.omitted_fields.insert(name.into());
353 }
354 }
355 }
356 view
357 }
358
359 pub(crate) async fn user_adapter_record(
361 &self,
362 user: S::User,
363 ) -> AuthResult<AdapterRecord<S::User>> {
364 use crate::AuthUser;
365 let registered = self
366 .extensions
367 .get::<crate::field_policy::AdapterFieldPolicies>();
368 let fields = registered.as_ref().map_or_else(
369 || {
370 crate::field_policy::SessionAdapterFields(Arc::new(
371 self.config.user.additional_fields.clone(),
372 ))
373 },
374 |fields| fields.user.clone(),
375 );
376 let output = fields
377 .record_output(
378 serde_json::to_value(
379 user.retained_user_view()
380 .cloned()
381 .unwrap_or_else(|| UserView::from(&user)),
382 )?,
383 user.additional_fields(),
384 serde_json::to_value(self.trusted_user_view(&user))?,
385 )
386 .await?;
387 Ok(AdapterRecord::with_output(user, output))
388 }
389
390 #[must_use]
393 pub fn filter_user_record(&self, record: AdapterRecord<S::User>) -> AdapterRecord<S::User> {
394 let registered = self.extensions.get::<crate::field_policy::UserFields>();
395 let fields = registered
396 .as_ref()
397 .map_or(&self.config.user.additional_fields, |fields| &fields.0.0);
398 let output = record.raw_snapshot().filter_returned(fields);
399 AdapterRecord::with_output(record.into_stored(), output)
400 }
401
402 pub fn account_view(
408 &self,
409 account: &impl AuthAccount,
410 ) -> AuthResult<serde_json::Map<String, serde_json::Value>> {
411 let serde_json::Value::Object(mut view) =
412 serde_json::to_value(crate::wire::AccountView::from(account))?
413 else {
414 return Err(AuthError::internal("Account view must be an object"));
415 };
416 let registered = self.extensions.get::<crate::field_policy::AccountFields>();
417 let fields = registered
418 .as_ref()
419 .map_or(&self.config.account.additional_fields, |fields| &fields.0.0);
420 let physical = account.additional_fields();
421 let values = account
422 .adapter_snapshot()
423 .map_or(&physical, AdapterOutput::values);
424 for (name, field) in fields {
425 _ = view.remove(name);
426 if field.returned
427 && let Some(value) = values.get(name).or_else(|| {
428 field
429 .field_name
430 .as_ref()
431 .and_then(|physical| values.get(physical))
432 })
433 {
434 _ = view.insert(name.clone(), value.clone());
435 }
436 }
437 for credential in [
438 "accessToken",
439 "refreshToken",
440 "idToken",
441 "accessTokenExpiresAt",
442 "refreshTokenExpiresAt",
443 "password",
444 ] {
445 _ = view.remove(credential);
446 }
447 Ok(view)
448 }
449
450 pub fn session_view(&self, session: &impl AuthSession) -> SessionView {
451 self.project_session_view(session, true)
452 }
453
454 pub fn trusted_session_view(&self, session: &impl AuthSession) -> SessionView {
455 self.project_session_view(session, false)
456 }
457
458 pub(in crate::plugin) fn project_session_view(
459 &self,
460 session: &impl AuthSession,
461 public: bool,
462 ) -> SessionView {
463 if let Some(view) = session.retained_session_view() {
464 let mut view = view.clone();
465 if public {
466 let registered = self.extensions.get::<crate::field_policy::SessionFields>();
467 let fields = registered
468 .as_ref()
469 .map_or(&self.config.session.additional_fields, |fields| &fields.0);
470 for (name, field) in fields {
471 if !field.returned {
472 _ = view.omitted_fields.insert(name.clone());
473 }
474 }
475 }
476 return view;
477 }
478 let mut view = SessionView::from(session);
479 let registered = self.extensions.get::<crate::field_policy::SessionFields>();
480 let fields = registered
481 .as_ref()
482 .map_or(&self.config.session.additional_fields, |fields| &fields.0);
483 view.extension_fields
484 .retain(|name, _| fields.contains_key(name));
485 if let Some(output) = session.adapter_snapshot().map(AdapterOutput::values) {
486 for name in fields.keys() {
487 _ = view.extension_fields.remove(name);
488 if let Some(value) = output.get(name) {
489 _ = view.extension_fields.insert(name.clone(), value.clone());
490 } else {
491 _ = view.omitted_fields.insert(name.clone());
492 }
493 }
494 }
495 for (name, field) in fields {
496 if public && !field.returned {
497 _ = view.omitted_fields.insert(name.clone());
498 }
499 }
500 let declared = |name: &str| fields.contains_key(name);
501 if self.feature_enabled("admin.enabled") || declared("impersonatedBy") {
502 if view.impersonated_by.is_none() {
503 insert_null(&mut view.extension_fields, "impersonatedBy");
504 }
505 } else {
506 view.impersonated_by = None;
507 }
508 if self.feature_enabled("organization.enabled") || declared("activeOrganizationId") {
509 if view.active_organization_id.is_none() {
510 insert_null(&mut view.extension_fields, "activeOrganizationId");
511 }
512 } else {
513 view.active_organization_id = None;
514 }
515
516 if self.feature_enabled("organization.teams.enabled") || declared("activeTeamId") {
517 if view.active_team_id.is_none() {
518 insert_null(&mut view.extension_fields, "activeTeamId");
519 }
520 } else {
521 view.active_team_id = None;
522 }
523 if self.config.session.stateless {
524 for (name, absent) in [
525 (
526 "activeOrganizationId",
527 view.active_organization_id.is_none(),
528 ),
529 ("impersonatedBy", view.impersonated_by.is_none()),
530 ] {
531 if absent
532 && view
533 .extension_fields
534 .get(name)
535 .is_none_or(serde_json::Value::is_null)
536 {
537 _ = view.omitted_fields.insert(name.into());
538 }
539 }
540 }
541 view
542 }
543
544 pub fn invitation_view(&self, invitation: &impl AuthInvitation) -> InvitationView {
545 let mut view = InvitationView::from(invitation);
546 if self.feature_enabled("organization.teams.enabled") {
547 if view.team_id.is_none() {
548 insert_null(&mut view.extension_fields, "teamId");
549 }
550 } else {
551 view.team_id = None;
552 }
553 view
554 }
555
556 pub(in crate::plugin) fn feature_enabled(&self, key: &str) -> bool {
557 self.metadata
558 .get(key)
559 .and_then(serde_json::Value::as_bool)
560 .unwrap_or(false)
561 }
562
563 pub fn email_provider(&self) -> AuthResult<&dyn EmailProvider> {
569 self.email_provider
570 .as_deref()
571 .ok_or_else(|| AuthError::config("No email provider configured"))
572 }
573
574 #[must_use]
576 pub fn session_manager(&self) -> SessionManager<S> {
577 SessionManager::new(Arc::clone(&self.config), Arc::clone(&self.database))
578 }
579
580 pub async fn require_session(
590 &self,
591 req: &impl SessionRequest,
592 ) -> AuthResult<(S::User, SessionView)> {
593 let (user, session, _) = self.require_session_with_refresh_state(req).await?;
594 Ok((user, session))
595 }
596
597 pub async fn require_cached_session(
604 &self,
605 req: &impl SessionRequest,
606 ) -> AuthResult<(AuthenticatedUser<S>, SessionView)> {
607 let read = crate::session::cookie_cache::runtime::authenticated(self, req, false)
608 .await
609 .map_err(|_| AuthError::Unauthenticated)?
610 .ok_or(AuthError::Unauthenticated)?;
611 Ok((read.user, read.session))
612 }
613
614 pub async fn require_cached_session_strict(
623 &self,
624 req: &impl SessionRequest,
625 ) -> AuthResult<(AuthenticatedUser<S>, SessionView)> {
626 let read = crate::session::cookie_cache::runtime::authenticated_with(
627 self,
628 req,
629 false,
630 crate::session::cookie_cache::runtime::CacheDecoding::MalformedIsMiss,
631 )
632 .await
633 .map_err(|error| {
634 if error.status_code() == 401 {
635 AuthError::Unauthenticated
636 } else {
637 error
638 }
639 })?
640 .ok_or(AuthError::Unauthenticated)?;
641 Ok((read.user, read.session))
642 }
643
644 pub async fn require_session_with_refresh_state(
652 &self,
653 req: &impl SessionRequest,
654 ) -> AuthResult<(S::User, SessionView, Option<bool>)> {
655 self.authenticated_session(req, true).await
656 }
657
658 pub async fn require_authoritative_cached_session(
662 &self,
663 req: &AuthRequest,
664 ) -> AuthResult<(AuthenticatedUser<S>, SessionView)> {
665 self.require_cached_session(&self.physical_request(req))
666 .await
667 }
668
669 pub async fn require_authoritative_session(
677 &self,
678 req: &impl SessionRequest,
679 ) -> AuthResult<(S::User, SessionView)> {
680 crate::session::cookie_cache::runtime::clear_established_session::<S>(req);
681 let (user, session, _) = self.authenticated_session(req, false).await?;
682 Ok((user, session))
683 }
684
685 pub async fn require_authoritative_session_record(
691 &self,
692 req: &impl SessionRequest,
693 ) -> AuthResult<(AdapterRecord<S::User>, SessionView)> {
694 crate::session::cookie_cache::runtime::clear_established_session::<S>(req);
695 let stateful = self.config.session.has_server_session_store();
696 let logical = req.endpoint_call().map(|call| {
697 call.clear_authenticated_session();
698 let mut nested = call.session_read_context();
699 if stateful {
700 let mut query = match nested.query().cloned() {
701 Some(crate::utils::json::JsValue::Object(query)) => query,
702 _ => indexmap::IndexMap::default(),
703 };
704 _ = query.insert(
705 "disableCookieCache".into(),
706 crate::utils::json::JsValue::Bool(true),
707 );
708 crate::endpoint::EndpointContextPatch {
709 query: Some(crate::utils::json::JsValue::Object(query)),
710 ..Default::default()
711 }
712 .apply(&mut nested);
713 }
714 nested
715 });
716 let authoritative = AuthoritativeSessionInput {
717 original: req,
718 logical: logical.clone(),
719 stateful,
720 };
721 let read =
722 crate::session::cookie_cache::runtime::authenticated(self, &authoritative, false);
723 let read = if let Some(logical) = logical {
724 crate::endpoint::with_endpoint_call_context(logical, read).await
725 } else {
726 read.await
727 }?
728 .ok_or(AuthError::Unauthenticated)?;
729 match read.user {
730 AuthenticatedUser::Stored(user) => Ok((user, read.session)),
731 AuthenticatedUser::Cached(user) if self.config.session.stateless => {
732 let user = S::user_from_cookie_cache(*user).ok_or_else(|| {
733 AuthError::config("This schema requires cache-aware session authority")
734 })?;
735 Ok((AdapterRecord::physical(user)?, read.session))
736 }
737 AuthenticatedUser::Cached(_) => Err(AuthError::Unauthenticated),
738 }
739 }
740
741 pub async fn session_user(&self, session: &impl AuthSession) -> AuthResult<Option<S::User>> {
747 use crate::AuthUser;
748 if let Some(user) = self.database.get_session_user(session.token()).await? {
749 if user.id() != session.user_id() {
750 return Err(AuthError::Unauthenticated);
751 }
752 return Ok(Some(user));
753 }
754 if self.config.session.secondary_storage.is_some()
755 && (!self.config.session.store_in_database || self.config.session.preserve_in_database)
756 {
757 return Ok(None);
758 }
759 self.database
760 .get_user_by_id(session.user_id().as_ref())
761 .await
762 }
763
764 fn physical_request(&self, req: &AuthRequest) -> AuthRequest {
767 crate::session::cookie_cache::runtime::clear_established_session::<S>(req);
768 let mut physical = req.clone();
769 physical.virtual_session = None;
770 if self.config.session.has_server_session_store() {
771 _ = physical
772 .query
773 .insert("disableCookieCache".into(), "true".into());
774 }
775 physical
776 }
777
778 pub(in crate::plugin) async fn authenticated_session(
779 &self,
780 req: &impl SessionRequest,
781 allow_virtual: bool,
782 ) -> AuthResult<(S::User, SessionView, Option<bool>)> {
783 if allow_virtual && let Some(session) = req.virtual_session(self) {
784 let user = if let Some(user) = req.authenticated_user::<S>(self) {
785 user
786 } else {
787 self.database
788 .get_user_by_id(&session.user_id)
789 .await?
790 .ok_or(AuthError::Unauthenticated)?
791 };
792 return Ok((user, session.clone(), None));
793 }
794 if self.config.session.stateless
795 && let Some(cache) = crate::session::cookie_cache::runtime::read(self, req).await?
796 {
797 crate::session::cookie_cache::runtime::renew_cache(self, req, &cache).await?;
798 let user = S::user_from_cookie_cache(cache.user).ok_or_else(|| {
799 AuthError::config("This schema requires cache-aware session authority")
800 })?;
801 return Ok((user, cache.session, None));
802 }
803 let session_manager = self.session_manager();
804
805 let suppressed = session_manager.request_disables_refresh(req);
806 let skip = req
807 .extensions()
808 .get::<crate::session::SessionRefreshSuppressed>()
809 .is_some();
810 let options = crate::session::SessionReadOptions {
811 allow_refresh: !suppressed && !self.config.session.defer_session_refresh && !skip,
812 cleanup_expired: !self.config.session.defer_session_refresh,
813 };
814 let Some(token) = session_manager.extract_session_token(req) else {
815 return Err(AuthError::Unauthenticated);
816 };
817 let read = session_manager
818 .read_session(&token, options)
819 .await
820 .map_err(|_| AuthError::Unauthenticated)?;
821 let Some(session) = read.session else {
822 self.queue_session_cleanup(req)?;
823 return Err(AuthError::Unauthenticated);
824 };
825 let user = self
826 .session_user(&session)
827 .await
828 .map_err(|_| AuthError::Unauthenticated)?;
829 let Some(user) = user else {
830 self.queue_session_cleanup(req)?;
831 return Err(AuthError::Unauthenticated);
832 };
833 if read.refreshed {
834 req.queue_response_header(
835 "Set-Cookie",
836 crate::utils::cookie_utils::create_session_cookie(session.token(), &self.config)?,
837 );
838 }
839 Ok((
840 user,
841 self.session_view(&session),
842 (self.config.session.defer_session_refresh && !suppressed)
843 .then_some(read.needs_refresh && !skip),
844 ))
845 }
846
847 pub(in crate::plugin) fn queue_session_cleanup(
848 &self,
849 req: &impl SessionRequest,
850 ) -> AuthResult<()> {
851 for cookie in crate::utils::cookie_utils::delete_session_cookie_headers(&self.config)? {
852 req.queue_response_header("Set-Cookie", cookie);
853 }
854 Ok(())
855 }
856
857 pub async fn session_without_refresh(
865 &self,
866 req: &impl SessionRequest,
867 ) -> AuthResult<Option<(S::User, SessionView)>> {
868 if let Some(session) = req.virtual_session(self) {
869 if let Some(user) = req.authenticated_user::<S>(self) {
870 return Ok(Some((user, session)));
871 }
872 return Ok(self
873 .database
874 .get_user_by_id(&session.user_id)
875 .await?
876 .map(|user| (user, session.clone())));
877 }
878 self.persistent_session(req).await
879 }
880
881 pub async fn persistent_session(
887 &self,
888 req: &impl SessionRequest,
889 ) -> AuthResult<Option<(S::User, SessionView)>> {
890 if self.config.session.stateless
891 && let Some(cache) = crate::session::cookie_cache::runtime::read(self, req).await?
892 {
893 let user = S::user_from_cookie_cache(cache.user).ok_or_else(|| {
894 AuthError::config("This schema requires cache-aware session authority")
895 })?;
896 return Ok(Some((user, cache.session)));
897 }
898 let Some(token) = self.session_manager().extract_session_token(req) else {
899 return Ok(None);
900 };
901 let Some(session) = self.database.get_session(&token).await? else {
902 return Ok(None);
903 };
904 if session.expires_at() <= chrono::Utc::now() {
905 return Ok(None);
906 }
907 let user = self.session_user(&session).await?;
908 let Some(user) = user else {
909 return Ok(None);
910 };
911 Ok(Some((user, self.session_view(&session))))
912 }
913}
914
915impl<S: AuthSchema> std::fmt::Debug for AuthContext<S> {
916 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
917 f.debug_struct("AuthContext").finish_non_exhaustive()
918 }
919}