async_session/
cookie_store.rs

1use crate::{async_trait, Result, Session, SessionStore};
2
3/// A session store that serializes the entire session into a Cookie.
4///
5/// # ***This is not recommended for most production deployments.***
6///
7/// This implementation uses [`bincode`](::bincode) to serialize the
8/// Session to decrease the size of the cookie. Note: There is a
9/// maximum of 4093 cookie bytes allowed _per domain_, so the cookie
10/// store is limited in capacity.
11///
12/// **Note:** Currently, the data in the cookie is only signed, but *not
13/// encrypted*. If the contained session data is sensitive and
14/// should not be read by a user, the cookie store is not an
15/// appropriate choice.
16///
17/// Expiry: `SessionStore::destroy_session` and
18/// `SessionStore::clear_store` are not meaningful for the
19/// CookieStore, and noop. Destroying a session must be done at the
20/// cookie setting level, which is outside of the scope of this crate.
21
22#[derive(Debug, Clone, Copy)]
23pub struct CookieStore;
24
25impl CookieStore {
26    /// constructs a new CookieStore
27    pub fn new() -> Self {
28        Self
29    }
30}
31
32#[async_trait]
33impl SessionStore for CookieStore {
34    async fn load_session(&self, cookie_value: String) -> Result<Option<Session>> {
35        let serialized = base64::decode(&cookie_value)?;
36        let session: Session = bincode::deserialize(&serialized)?;
37        Ok(session.validate())
38    }
39
40    async fn store_session(&self, session: Session) -> Result<Option<String>> {
41        let serialized = bincode::serialize(&session)?;
42        Ok(Some(base64::encode(serialized)))
43    }
44
45    async fn destroy_session(&self, _session: Session) -> Result {
46        Ok(())
47    }
48
49    async fn clear_store(&self) -> Result {
50        Ok(())
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use async_std::task;
58    use std::time::Duration;
59    #[async_std::test]
60    async fn creating_a_new_session_with_no_expiry() -> Result {
61        let store = CookieStore::new();
62        let mut session = Session::new();
63        session.insert("key", "Hello")?;
64        let cloned = session.clone();
65        let cookie_value = store.store_session(session).await?.unwrap();
66        let loaded_session = store.load_session(cookie_value).await?.unwrap();
67        assert_eq!(cloned.id(), loaded_session.id());
68        assert_eq!("Hello", &loaded_session.get::<String>("key").unwrap());
69        assert!(!loaded_session.is_expired());
70        assert!(loaded_session.validate().is_some());
71        Ok(())
72    }
73
74    #[async_std::test]
75    async fn updating_a_session() -> Result {
76        let store = CookieStore::new();
77        let mut session = Session::new();
78
79        session.insert("key", "value")?;
80        let cookie_value = store.store_session(session).await?.unwrap();
81
82        let mut session = store.load_session(cookie_value.clone()).await?.unwrap();
83        session.insert("key", "other value")?;
84
85        let new_cookie_value = store.store_session(session).await?.unwrap();
86        let session = store.load_session(new_cookie_value).await?.unwrap();
87        assert_eq!(&session.get::<String>("key").unwrap(), "other value");
88
89        Ok(())
90    }
91
92    #[async_std::test]
93    async fn updating_a_session_extending_expiry() -> Result {
94        let store = CookieStore::new();
95        let mut session = Session::new();
96        session.expire_in(Duration::from_secs(1));
97        let original_expires = session.expiry().unwrap().clone();
98        let cookie_value = store.store_session(session).await?.unwrap();
99
100        let mut session = store.load_session(cookie_value.clone()).await?.unwrap();
101
102        assert_eq!(session.expiry().unwrap(), &original_expires);
103        session.expire_in(Duration::from_secs(3));
104        let new_expires = session.expiry().unwrap().clone();
105        let cookie_value = store.store_session(session).await?.unwrap();
106
107        let session = store.load_session(cookie_value.clone()).await?.unwrap();
108        assert_eq!(session.expiry().unwrap(), &new_expires);
109
110        task::sleep(Duration::from_secs(3)).await;
111        assert_eq!(None, store.load_session(cookie_value).await?);
112
113        Ok(())
114    }
115
116    #[async_std::test]
117    async fn creating_a_new_session_with_expiry() -> Result {
118        let store = CookieStore::new();
119        let mut session = Session::new();
120        session.expire_in(Duration::from_secs(3));
121        session.insert("key", "value")?;
122        let cloned = session.clone();
123
124        let cookie_value = store.store_session(session).await?.unwrap();
125
126        let loaded_session = store.load_session(cookie_value.clone()).await?.unwrap();
127        assert_eq!(cloned.id(), loaded_session.id());
128        assert_eq!("value", &*loaded_session.get::<String>("key").unwrap());
129
130        assert!(!loaded_session.is_expired());
131
132        task::sleep(Duration::from_secs(3)).await;
133        assert_eq!(None, store.load_session(cookie_value).await?);
134
135        Ok(())
136    }
137}