pavex_session/
middleware.rs

1use pavex::{
2    Response,
3    cookie::{Processor, ResponseCookies},
4    post_process,
5};
6use tracing::Span;
7
8use crate::{Session, errors::FinalizeError};
9
10/// A post-processing middleware to attach a session cookie to the outgoing response, if needed.
11///
12/// It will also sync the session server-side state with the chosen storage backend.
13#[tracing::instrument(
14    name = "Finalize session",
15    level = tracing::Level::DEBUG, skip_all,
16    fields(session.cookie.set = tracing::field::Empty)
17)]
18#[post_process]
19pub async fn finalize_session<'store>(
20    response: Response,
21    response_cookies: &mut ResponseCookies,
22    processor: &Processor,
23    mut session: Session<'store>,
24) -> Result<Response, FinalizeError> {
25    // If the client-side session state is not empty, we require encryption
26    // to minimize the risk of exposure for sensitive data.
27    let must_encrypt = !session.client().is_empty();
28    let cookie = session.finalize().await?;
29
30    Span::current().record("session.cookie.set", cookie.is_some());
31
32    if let Some(cookie) = cookie {
33        let will_encrypt = processor.will_encrypt(cookie.name());
34
35        if must_encrypt && !will_encrypt {
36            return Err(FinalizeError::EncryptionRequired {
37                cookie_name: cookie.name().to_string(),
38            });
39        }
40        if !(will_encrypt || processor.will_sign(cookie.name())) {
41            return Err(FinalizeError::CryptoRequired {
42                cookie_name: cookie.name().to_string(),
43            });
44        }
45        response_cookies.insert(cookie);
46    }
47
48    Ok(response)
49}