Skip to main content

CookieSessionStore

Struct CookieSessionStore 

Source
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 storeno
Works across instancesyes, with no shared infrastructure
Size limit~3.5 KB, hard
Can be revoked server-sideno
Cost per requestthe 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

Source

pub fn new(crypt: Encryption) -> Self

A store encrypting with crypt.

Source

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.

Source

pub fn max_bytes(&self) -> usize

The size limit in force.

Trait Implementations§

Source§

impl Debug for CookieSessionStore

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl SessionStore for CookieSessionStore

Source§

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<()>>

A no-op: there is nowhere to write to.

Source§

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.

Source§

fn name(&self) -> &str

A label for diagnostics — "memory", "database", "cache", "cookie".
Source§

fn is_client_side(&self) -> bool

Whether the session travels in the cookie rather than being stored here. Read more
Source§

fn encode(&self, id: &str, data: &SessionData) -> Result<String>

The cookie value carrying this session. Read more
Source§

fn decode(&self, value: &str) -> Result<(String, Option<SessionData>)>

Recover a session id — and, for a client-side store, its data — from a cookie value. Read more
Source§

fn gc(&self) -> BoxFuture<'_, Result<u64>>

Discard every expired session. Called periodically, not per request.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Event for T
where T: Send + Sync + 'static,

Source§

fn event_name() -> &'static str
where Self: Sized,

A human-readable name, used by wildcard listeners and log output. Always the Rust type path; see the trait docs.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more