Skip to main content

topcoat_cookie/
signed.rs

1use cookie::{Cookie, CookieJar as RawCookieJar, Key};
2
3use crate::Cookies;
4
5/// A [`Cookies`] adapter that signs cookies written through it and verifies
6/// cookies read through it, using a [`Key`].
7///
8/// Signing detects changes to a cookie's value while leaving it readable by
9/// the client. Create a signed jar with [`Cookies::signed`] or
10/// [`signed_cookies`](crate::signed_cookies).
11///
12/// Reads return `None` for missing cookies or invalid signatures. This adapter
13/// can be combined with prefixes and attribute defaults.
14#[derive(Debug, Clone, Copy)]
15pub struct SignedJar<'key, J> {
16    inner: J,
17    key: &'key Key,
18}
19
20impl<'key, J> SignedJar<'key, J> {
21    pub(crate) fn new(inner: J, key: &'key Key) -> Self {
22        Self { inner, key }
23    }
24}
25
26impl<J: Cookies> Cookies for SignedJar<'_, J> {
27    fn get(&self, name: &str) -> Option<Cookie<'static>> {
28        let raw = self.inner.get(name)?;
29        // Reuse the cookie crate's verification by routing the raw cookie
30        // through a throwaway jar seeded with it as an original.
31        let name = raw.name().to_owned();
32        let mut jar = RawCookieJar::new();
33        jar.add_original(raw);
34        jar.signed(self.key).get(&name)
35    }
36
37    fn add<C: Into<Cookie<'static>>>(&self, cookie: C) {
38        let cookie = cookie.into();
39        // Sign via a throwaway jar, then forward the signed cookie inward so
40        // any further layers (prefix, defaults) still apply.
41        let name = cookie.name().to_owned();
42        let mut jar = RawCookieJar::new();
43        jar.signed_mut(self.key).add(cookie);
44        if let Some(signed) = jar.get(&name).cloned() {
45            self.inner.add(signed);
46        }
47    }
48
49    fn remove<C: Into<Cookie<'static>>>(&self, cookie: C) {
50        // Removal cookies match by name only; the signature is irrelevant.
51        self.inner.remove(cookie);
52    }
53}