1use crate::cache::ResolvedUser;
2use crate::provider::UserProviderService;
3use modo::app::AppState;
4use modo::axum::extract::FromRequestParts;
5use modo::axum::http::request::Parts;
6use modo::{Error, HttpError};
7use modo_session::SessionManager;
8use std::ops::Deref;
9use std::sync::Arc;
10
11async fn resolve_user<U: Clone + Send + Sync + 'static>(
16 parts: &mut Parts,
17 state: &AppState,
18) -> Result<Option<U>, Error> {
19 if let Some(cached) = parts.extensions.get::<ResolvedUser<U>>() {
21 tracing::debug!(cache_hit = true, "auth user resolved from extension cache");
22 return Ok(Some((*cached.0).clone()));
23 }
24
25 let session = SessionManager::from_request_parts(parts, state)
26 .await
27 .map_err(|_| Error::internal("Auth requires session middleware"))?;
28
29 let user_id = match session.user_id().await {
30 Some(id) => id,
31 None => {
32 tracing::debug!("no session user_id, skipping auth resolution");
33 return Ok(None);
34 }
35 };
36
37 let provider = state
38 .services
39 .get::<UserProviderService<U>>()
40 .ok_or_else(|| {
41 Error::internal(format!(
42 "UserProviderService<{}> not registered",
43 std::any::type_name::<U>()
44 ))
45 })?;
46
47 let user = provider.find_by_id(&user_id).await?;
48
49 if let Some(ref u) = user {
50 tracing::debug!(user_id = %user_id, cache_hit = false, "auth user resolved from provider");
51 parts.extensions.insert(ResolvedUser(Arc::new(u.clone())));
52 } else {
53 tracing::warn!(user_id = %user_id, "session references non-existent user");
54 }
55
56 Ok(user)
57}
58
59#[derive(Clone)]
69pub struct Auth<U: Clone + Send + Sync + 'static>(
70 pub U,
72);
73
74impl<U: Clone + Send + Sync + 'static> Deref for Auth<U> {
75 type Target = U;
76
77 fn deref(&self) -> &Self::Target {
78 &self.0
79 }
80}
81
82impl<U: Clone + Send + Sync + 'static> FromRequestParts<AppState> for Auth<U> {
83 type Rejection = Error;
84
85 async fn from_request_parts(
86 parts: &mut Parts,
87 state: &AppState,
88 ) -> Result<Self, Self::Rejection> {
89 let user = resolve_user::<U>(parts, state)
90 .await?
91 .ok_or_else(|| Error::from(HttpError::Unauthorized))?;
92 Ok(Auth(user))
93 }
94}
95
96#[derive(Clone)]
109pub struct OptionalAuth<U: Clone + Send + Sync + 'static>(
110 pub Option<U>,
112);
113
114impl<U: Clone + Send + Sync + 'static> Deref for OptionalAuth<U> {
115 type Target = Option<U>;
116
117 fn deref(&self) -> &Self::Target {
118 &self.0
119 }
120}
121
122impl<U: Clone + Send + Sync + 'static> FromRequestParts<AppState> for OptionalAuth<U> {
123 type Rejection = Error;
124
125 async fn from_request_parts(
126 parts: &mut Parts,
127 state: &AppState,
128 ) -> Result<Self, Self::Rejection> {
129 let user = resolve_user::<U>(parts, state).await?;
130 Ok(OptionalAuth(user))
131 }
132}