tower-sessions-cookie-store
Cookie-backed session persistence for tower-sessions.
This crate provides CookieSessionManagerLayer, a Tower layer for cookie-backed sessions. It
integrates with tower-sessions and works with Axum extractors.
Install
Features
signed(default): integrity-protected cookies (tamper-evident). Cookie contents are readable by the client. Provided bycookie.private: encrypted + authenticated cookies (confidentiality + integrity). Provided bycookie.key-expansion: enablesKey::derive_from()for deriving a cookieKeyfrom a 32-byte master key. Requiressignedand/orprivate. Provided bycookie.dangerous-plaintext: plaintext cookies (no integrity, no confidentiality). Intended only for testing/debugging.
Usage (Axum)
For more usage examples, see the examples/ directory.
Signed cookies (default)
use ;
use ;
async
let secret_key = generate; // store this someplace safe
let config = default
.with_secure; // set true in production (HTTPS)
let router = new
.route
.layer;
Configuration
CookieSessionConfig controls cookie attributes and session persistence behavior:
name(default:"session")http_only(default:true)same_site(default:SameSite::Strict)secure(default:true)path(default:"/")domain(default: none)expiry(default: none)Expiry::OnSessionEnd: noMax-Ageis setExpiry::OnInactivity(d): expiration is computed from the last time the session was modified (reads do not count). If you need sliding expiry on every request, setalways_save = true.Expiry::AtDateTime(t):Max-Ageis set from the record expiry (absolute unless changed)
always_save(default:false): whentrue, refreshes cookie/expiry on every request even if the session is not modifiedmax_cookie_bytes(default:4096): maximum encoded cookie value sizeclear_on_decode_error(default:true): clears invalid/expired/undecodable cookies
Behavior notes
- Saves happen after your handler returns. If encoding the cookie fails (e.g., the encoded session
exceeds
max_cookie_bytes), the layer responds with500and does not emitSet-Cookie. - When
clear_on_decode_erroristrue, a bad/expired/undecodable incoming cookie is actively cleared so the client stops sending it.
Cookie format
The cookie value encodes the full session record using a versioned, base64url-encoded JSON envelope. The format is an implementation detail and may change between releases.
For testing/debugging, the crate exposes tower_sessions_cookie_store::{encode_record, decode_record}.
Benefits of cookie-backed session storage
- Stateless: no server-side session store to operate.
- No per-request database/network I/O to load session state.
- No backend database/table/indexes/migrations needed for session storage.
Security notes
- Cookie sessions are bearer tokens. If a cookie is stolen, it can be replayed until it expires.
- Unlike server-side session stores (e.g. Redis), cookie-backed sessions have no server-side state to revoke. Any previously issued cookie remains valid until it expires, even if the application later rotates the session ID, updates session data, or "logs out" the user by clearing the cookie (the client will stop sending it, but an attacker holding a copy can still replay it until expiry).
- The
dangerous-plaintextfeature offers no tamper resistance. A client can trivially edit the cookie to escalate privileges and impersonate other users (including staff/admin). Do not enable or use it in real applications.
FAQ
- How can I log everyone out at once?
- Rotate the signing/encryption key. All existing cookies will fail verification and be rejected.
- How can I revoke one user’s session?
- You can’t centrally revoke a single cookie-only session. One option: store a version/token on the user record and also in the session; reject the session when they don’t match and force the client to reauthenticate. See the security notes above.
- Why did I get a 500 when saving a session?
- The encoded cookie likely exceeded
max_cookie_bytes(default 4096) or failed to encode/verify; the layer returns 500 and does not set a cookie.
- The encoded cookie likely exceeded
- Can I store large objects or lots of data?
- No. Cookies are limited (~4 KB by default). Use a server-side store if you need larger payloads.
- Can I use this cross-site?
- Set
SameSite::Noneandsecure = true(required by browsers). It is very unlikely that you actually want to do this. Use caution.
- Set
- Do I need HTTPS in production?
- Yes; leave
secure = trueso browsers only send the cookie over HTTPS. Only disable it locally for development.
- Yes; leave
- Can I allow JavaScript to read my session cookies?
- Strongly discouraged. Disabling HttpOnly (via
config.with_http_only(false)) lets any injected script read and exfiltrate the bearer token, enabling replay. Only do this if you have a very specific, XSS-hardened reason.
- Strongly discouraged. Disabling HttpOnly (via
License
MIT. See LICENSE.