blackbull.middleware.session¶
blackbull.middleware.session
¶
Signed-cookie session middleware (HMAC-SHA256).
The session payload lives entirely in the cookie sent to the client; the server keeps no per-session state. Every value the client could otherwise tamper with is HMAC-signed by a secret the server alone knows, so:
- a client that modifies the cookie sees the next request treated as a fresh empty session (signature check fails),
- the server never reads, writes, or replays anything else — no DB hit, no Redis round-trip, no in-process dict to keep consistent across workers,
- sessions survive worker restarts / horizontal scaling for free (any worker that knows the secret can validate any cookie).
The trade-offs compared with server-side session stores:
- Cookie size is bounded (browsers cap to ~4 KiB, often less in practice). Sessions storing large objects don't fit.
- No server-side invalidation: revoking a cookie before it expires requires either rotating the secret (kills every session) or a separate revocation list. Acceptable for many apps.
Wire format (Base64URL-encoded payload + "." + hex MAC)::
<urlsafe-b64-no-padding(json-bytes)>.<hex-hmac-sha256(json-bytes)>
The cookie is dispatched on every response that touched scope['session']
in a way that modified it (see :class:_SessionDict); unmodified sessions
trigger no Set-Cookie header, so a 304 / cache-friendly response stays
cache-friendly.
Usage::
from blackbull.middleware import Session
app.use(Session()) # reads BB_SESSION_SECRET
app.use(Session(secret=b'\x...')) # explicit secret
@app.route(path='/')
async def index(scope, receive, send):
scope['session']['user'] = 'alice' # writes Set-Cookie
await send({'type': 'http.response.start', ...})
await send({'type': 'http.response.body', ...})
Session
¶
ASGI middleware that maintains a signed-cookie session.
Parameters¶
secret:
The HMAC key. Bytes or str. When omitted, BB_SESSION_SECRET
is read from the environment. A missing / empty secret raises at
construction — no insecure default.
cookie_name:
Name of the cookie carrying the session payload. Default
'session'.
max_age:
Cookie Max-Age in seconds. When set, the cookie is signed
with a server-side timestamp; values older than max_age
seconds are treated as expired (empty session). None means
a session cookie that lives only as long as the browser is open.
secure:
Set the Secure attribute so the cookie is only sent over
HTTPS. Default True; set False for local-only dev.
httponly:
Set the HttpOnly attribute (JavaScript can't read the
cookie). Default True.
samesite:
Strict / Lax / None. Default 'Lax'.
path:
Cookie Path. Default /.
binascii_error_class()
¶
Return binascii.Error lazily — avoids a top-level import.