actix_session/storage/
cookie.rs

1use actix_web::cookie::time::Duration;
2use anyhow::Error;
3
4use super::SessionKey;
5use crate::storage::{
6    interface::{LoadError, SaveError, SessionState, UpdateError},
7    SessionStore,
8};
9
10/// Use the session key, stored in the session cookie, as storage backend for the session state.
11///
12/// ```no_run
13/// use actix_web::{cookie::Key, web, App, HttpServer, HttpResponse, Error};
14/// use actix_session::{SessionMiddleware, storage::CookieSessionStore};
15///
16/// // The secret key would usually be read from a configuration file/environment variables.
17/// fn get_secret_key() -> Key {
18///     # todo!()
19///     // [...]
20/// }
21///
22/// #[actix_web::main]
23/// async fn main() -> std::io::Result<()> {
24///     let secret_key = get_secret_key();
25///     HttpServer::new(move ||
26///             App::new()
27///             .wrap(SessionMiddleware::new(CookieSessionStore::default(), secret_key.clone()))
28///             .default_service(web::to(|| HttpResponse::Ok())))
29///         .bind(("127.0.0.1", 8080))?
30///         .run()
31///         .await
32/// }
33/// ```
34///
35/// # Limitations
36/// Cookies are subject to size limits so we require session keys to be shorter than 4096 bytes.
37/// This translates into a limit on the maximum size of the session state when using cookies as
38/// storage backend.
39///
40/// The session cookie can always be inspected by end users via the developer tools exposed by their
41/// browsers. We strongly recommend setting the policy to [`CookieContentSecurity::Private`] when
42/// using cookies as storage backend.
43///
44/// There is no way to invalidate a session before its natural expiry when using cookies as the
45/// storage backend.
46///
47/// [`CookieContentSecurity::Private`]: crate::config::CookieContentSecurity::Private
48#[derive(Default)]
49#[non_exhaustive]
50pub struct CookieSessionStore;
51
52impl SessionStore for CookieSessionStore {
53    async fn load(&self, session_key: &SessionKey) -> Result<Option<SessionState>, LoadError> {
54        serde_json::from_str(session_key.as_ref())
55            .map(Some)
56            .map_err(anyhow::Error::new)
57            .map_err(LoadError::Deserialization)
58    }
59
60    async fn save(
61        &self,
62        session_state: SessionState,
63        _ttl: &Duration,
64    ) -> Result<SessionKey, SaveError> {
65        let session_key = serde_json::to_string(&session_state)
66            .map_err(anyhow::Error::new)
67            .map_err(SaveError::Serialization)?;
68
69        session_key
70            .try_into()
71            .map_err(Into::into)
72            .map_err(SaveError::Other)
73    }
74
75    async fn update(
76        &self,
77        _session_key: SessionKey,
78        session_state: SessionState,
79        ttl: &Duration,
80    ) -> Result<SessionKey, UpdateError> {
81        self.save(session_state, ttl)
82            .await
83            .map_err(|err| match err {
84                SaveError::Serialization(err) => UpdateError::Serialization(err),
85                SaveError::Other(err) => UpdateError::Other(err),
86            })
87    }
88
89    async fn update_ttl(&self, _session_key: &SessionKey, _ttl: &Duration) -> Result<(), Error> {
90        Ok(())
91    }
92
93    async fn delete(&self, _session_key: &SessionKey) -> Result<(), anyhow::Error> {
94        Ok(())
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use crate::{storage::utils::generate_session_key, test_helpers::acceptance_test_suite};
102
103    #[actix_web::test]
104    async fn test_session_workflow() {
105        acceptance_test_suite(CookieSessionStore::default, false).await;
106    }
107
108    #[actix_web::test]
109    async fn loading_a_random_session_key_returns_deserialization_error() {
110        let store = CookieSessionStore::default();
111        let session_key = generate_session_key();
112        assert!(matches!(
113            store.load(&session_key).await.unwrap_err(),
114            LoadError::Deserialization(_),
115        ));
116    }
117}