šŸ™ tako
Extractors

Cookie extractors

Read and write request cookies — plain CookieJar, HMAC-signed CookieSigned, and encrypted CookiePrivate, with key rotation.

Three cookie jars cover three trust levels. All parse every incoming Cookie: header into a jar. Return the jar, or jar.with_response(body), to emit its additions and removals as separate Set-Cookie headers. Values are plaintext, signed, or encrypted depending on the jar.

ExtractorCatalog nameProtection
CookieJarCookieJarnone (plaintext)
CookieSignedSignedCookieJarHMAC integrity — readable but tamper-evident
CookiePrivatePrivateCookieJarencryption — unreadable and tamper-proof

CookieJar

Plain cookie reading and writing. Wraps the cookie crate's jar and parses all Cookie: headers on extraction. Its error type is Infallible.

use cookie::Cookie;
use tako::extractors::cookie_jar::CookieJar;

async fn handle(mut jar: CookieJar) -> CookieJar {
  if let Some(session) = jar.get("session_id") {
    println!("session: {}", session.value());
  }
  jar.add(Cookie::new("visited", "true"));
  jar
}

Use jar.with_response("saved") to retain a response body, status, and existing headers while appending cookie changes. Unchanged request cookies are not echoed, and dropping a jar does not write cookies to the response. Invalid cookie names, wire values, or attributes produce a 500 response. Plain and signed values must contain valid cookie bytes; encode text with spaces or non-ASCII characters before storing it. Private values are encrypted before serialization and can contain arbitrary text.

remove(name) copies the existing cookie's Path / Domain into the removal marker so the browser actually invalidates it (rather than leaving an identically-named cookie on another path or the apex domain in place).

CookieSigned

HMAC-signed cookies (the catalog's SignedCookieJar). Values stay readable by the client but any tampering is detected on read. Construct with a cookie::Key; add signs and get verifies.

use cookie::{Cookie, Key};
use tako::extractors::cookie_signed::CookieSigned;

let key = Key::generate();
let mut signed = CookieSigned::new(key);

signed.add(Cookie::new("username", "alice"));
if let Some(c) = signed.get("username") {
  assert_eq!(c.value(), "alice"); // verified
}

CookieSignedError covers MissingKey / InvalidKey (→ 500) and VerificationFailed / InvalidCookieFormat / InvalidSignature (→ 400).

CookiePrivate

Encrypted cookies (the catalog's PrivateCookieJar). Values are unreadable by the client and tamper-proof. Same shape as CookieSigned — add encrypts, get decrypts.

use cookie::{Cookie, Key};
use tako::extractors::cookie_private::CookiePrivate;

let key = Key::generate();
let mut private = CookiePrivate::new(key);

private.add(Cookie::new("secret", "sensitive_data"));
if let Some(c) = private.get("secret") {
  assert_eq!(c.value(), "sensitive_data"); // decrypted
}

CookiePrivateError covers MissingKey / InvalidKey (→ 500) and DecryptionFailed / InvalidCookieFormat (→ 400). Use CookiePrivate whenever the value is sensitive (session tokens, user identifiers); use CookieSigned when the value may be visible but must not be forged.

Key rotation with KeyRing

Both the signed and private jars support a KeyRing so you can rotate keys without invalidating cookies signed under an older key. The ring has one active key (used to sign / encrypt new cookies) plus any number of previous keys (tried for verification / decryption only); each key carries a string kid.

use cookie::Key;
use tako::extractors::cookie_signed::{CookieSigned, KeyRing};

let ring = KeyRing::new("v2", Key::generate())
  .with_previous("v1", Key::generate());

let signed = CookieSigned::with_ring(ring);

get_with_kid(name) returns which kid admitted a cookie (handy for logging mid-rotation). KeyRing::revoke(kid) drops a previous key once it is past its retention window, and previous_kids() lists the currently trusted ids. The same KeyRing type from cookie_signed is reused by CookiePrivate::with_ring.

When a jar is extracted from a request, a KeyRing placed in request extensions takes precedence over a bare single key — the standard way to provide the key material is to insert it via middleware on the route group.

  • Enforcing cookie-based identity across a route group — see the auth middleware.
  • Cookie sessions are also available as a bundled middleware.

Last updated on

On this page