axess_core/session/layer/
service.rs1#[cfg(feature = "device")]
9use crate::device::resolver::ErasedDeviceResolver;
10use crate::session::binding::{self, SessionBinding};
11use crate::session::config::SessionConfig;
12use crate::session::layer::SessionLayer;
13use crate::session::layer::handle::{SessionHandle, SessionInner};
14use crate::session::layer::lifecycle::{build_set_cookie, finalize_session, load_session};
15use crate::session::layer::signing::SigningKeys;
16use crate::session::store::SessionStore;
17use axum::{body::Body, http::Request, response::Response};
18use std::{
19 future::Future,
20 pin::Pin,
21 sync::Arc,
22 task::{Context, Poll},
23};
24use tokio::sync::RwLock;
25use tower::{Layer, Service};
26
27#[doc(hidden)]
36#[derive(Clone)]
37pub struct SessionService<S, Inner> {
38 inner: Inner,
39 store: S,
40 signing_keys: Arc<SigningKeys>,
41 config: Arc<SessionConfig>,
44 binding: Option<Arc<dyn SessionBinding>>,
45 metrics: Option<Arc<dyn crate::metrics::AuthnMetrics>>,
46 #[cfg(feature = "device")]
47 device_resolver: Option<Arc<dyn ErasedDeviceResolver>>,
48}
49
50impl<S, Inner> Layer<Inner> for SessionLayer<S>
51where
52 S: SessionStore + Clone,
53{
54 type Service = SessionService<S, Inner>;
55
56 fn layer(&self, inner: Inner) -> Self::Service {
57 SessionService {
58 inner,
59 store: self.store.clone(),
60 signing_keys: self.signing_keys.clone(),
61 config: self.config.clone(),
62 binding: self.binding.clone(),
63 metrics: self.metrics.clone(),
64 #[cfg(feature = "device")]
65 device_resolver: self.device_resolver.clone(),
66 }
67 }
68}
69
70impl<S, Inner, ResBody> Service<Request<Body>> for SessionService<S, Inner>
71where
72 S: SessionStore + Clone + Send + Sync + 'static,
73 S::Error: Send + Sync + 'static,
74 Inner: Service<Request<Body>, Response = Response<ResBody>> + Clone + Send + 'static,
75 Inner::Future: Send + 'static,
76 Inner::Error: Send + 'static,
77 ResBody: Send + 'static,
78{
79 type Response = Response<ResBody>;
80 type Error = Inner::Error;
81 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
82
83 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
84 self.inner.poll_ready(cx)
85 }
86
87 fn call(&mut self, mut req: Request<Body>) -> Self::Future {
88 let store = self.store.clone();
89 let config = self.config.clone();
90 let signing_keys = self.signing_keys.clone();
91 let session_binding = self.binding.clone();
92 let metrics = self.metrics.clone();
93 #[cfg(feature = "device")]
94 let device_resolver = self.device_resolver.clone();
95
96 let mut inner = self.inner.clone();
98 std::mem::swap(&mut inner, &mut self.inner);
99
100 let current_fingerprint = session_binding
104 .as_deref()
105 .and_then(|b| binding::compute_fingerprint(b, &req, &signing_keys.fingerprint));
106
107 Box::pin(async move {
108 let load = load_session(
110 &store,
111 &signing_keys,
112 &config,
113 metrics.as_deref(),
114 req.headers(),
115 current_fingerprint.as_deref(),
116 )
117 .await;
118
119 #[cfg(feature = "device")]
137 let (load, device_changed) = {
138 let mut load = load;
139 let dc = if let Some(ref resolver) = device_resolver {
140 let (parts, body) = req.into_parts();
141 let resolved = resolver.resolve_erased(&parts).await;
142 req = Request::from_parts(parts, body);
143 let differs = resolved != load.data.device_id;
144 if differs {
145 load.data.device_id = resolved;
146 }
147 differs
148 } else {
149 false
150 };
151 (load, dc)
152 };
153 #[cfg(not(feature = "device"))]
154 let device_changed = false;
155
156 let pending_fp = if session_binding.is_some() && load.data.fingerprint.is_none() {
161 current_fingerprint.clone()
162 } else {
163 None
164 };
165
166 let inner_state = SessionInner {
167 id: load.id,
168 data: load.data,
169 modified: load.binding_invalidated || device_changed,
170 regenerate: load.binding_invalidated,
171 pre_cycle_id: None,
172 pending_fingerprint: pending_fp,
173 max_custom_bytes: config.max_custom_bytes,
174 };
175 let handle = SessionHandle(Arc::new(RwLock::new(inner_state)));
176 req.extensions_mut().insert(handle.clone());
177
178 let response = inner.call(req).await?;
180
181 let outcome = finalize_session(
183 &store,
184 &config,
185 metrics.as_deref(),
186 &handle,
187 load.existing_id,
188 )
189 .await;
190
191 let mut response = response;
195 if outcome.session_changed
196 && let Some(hv) = build_set_cookie(&signing_keys, &config, outcome.final_id)
197 {
198 response
199 .headers_mut()
200 .append(axum::http::header::SET_COOKIE, hv);
201 }
202
203 Ok(response)
204 })
205 }
206}