pub struct CookieSessionStore { /* private fields */ }Expand description
Sessions kept entirely in the cookie, encrypted.
No server-side state at all: nothing to store, nothing to expire, nothing to share between instances. That is the whole appeal, and every limitation below follows from it.
| Needs a store | no |
| Works across instances | yes, with no shared infrastructure |
| Size limit | ~3.5 KB, hard |
| Can be revoked server-side | no |
| Cost per request | the cookie, on every request, in both directions |
§What you give up
Revocation. A session you cannot see is a session you cannot end. There is no “log out all my devices”, no invalidating everything when a password changes, and a stolen cookie works until it expires. If any of that matters, use a server-side store.
Size. The limit is small and the failure is at write time. Putting a user’s cart in the session will eventually exceed it.
Freshness. The client holds the only copy, so a session it does not send back is simply gone, and one it sends from an old tab is stale.
§What protects it
The cookie is encrypted, so the client cannot read it or change it — tampering fails the AEAD tag and the session is discarded. Which means the key ring matters twice over here: rotating a key out ends every session it wrote.
use rainier_crypt::{Encryption, Key, KeyRing};
use rainier_session::{CookieSessionStore, Session, SessionStore};
let store = CookieSessionStore::new(Encryption::from_keys(KeyRing::new(Key::generate())));
let session = Session::new();
session.put("user_id", 42u64)?;
// What would be sent as the cookie value…
let cookie = store.encode(&session.id(), &session.age_and_take())?;
// The field name rather than the value: base64 of random bytes contains a
// given two-character run often enough that asserting on `"42"` fails a
// run every few hundred for no reason at all.
assert!(!cookie.contains("user_id"), "the client cannot read it");
// …and what comes back.
let (id, data) = store.decode(&cookie)?;
assert_eq!(id, session.id());
assert_eq!(data.unwrap().values["user_id"], 42);Implementations§
Source§impl CookieSessionStore
impl CookieSessionStore
Sourcepub fn new(crypt: Encryption) -> Self
pub fn new(crypt: Encryption) -> Self
A store encrypting with crypt.
Sourcepub fn with_max_bytes(self, max_bytes: usize) -> Self
pub fn with_max_bytes(self, max_bytes: usize) -> Self
Allow a larger cookie than the conservative default.
Only worth touching if you know what your clients and any proxy in front of them accept. A cookie over the browser’s limit is silently dropped, and a session that silently does not persist is a bad afternoon.
Trait Implementations§
Source§impl Debug for CookieSessionStore
impl Debug for CookieSessionStore
Source§impl SessionStore for CookieSessionStore
impl SessionStore for CookieSessionStore
Source§fn read<'a>(
&'a self,
_id: &'a str,
) -> BoxFuture<'a, Result<Option<SessionData>>>
fn read<'a>( &'a self, _id: &'a str, ) -> BoxFuture<'a, Result<Option<SessionData>>>
Never called: the data is in the cookie, and
decode has already produced it.
Source§fn write<'a>(
&'a self,
_id: &'a str,
_data: &'a SessionData,
) -> BoxFuture<'a, Result<()>>
fn write<'a>( &'a self, _id: &'a str, _data: &'a SessionData, ) -> BoxFuture<'a, Result<()>>
A no-op: there is nowhere to write to.
Source§fn destroy<'a>(&'a self, _id: &'a str) -> BoxFuture<'a, Result<()>>
fn destroy<'a>(&'a self, _id: &'a str) -> BoxFuture<'a, Result<()>>
A no-op.
This is the limitation to understand before choosing this store: a session held only by the client cannot be destroyed server-side. The middleware still rotates the id and clears the bag on logout, which replaces the cookie in that browser — but a copy taken beforehand keeps working until it expires.