Bastion.Session
Session — cookie-backed session storage for Bastion.
Sessions are stored as a signed cookie named "_session". The cookie value is: "<hmac_hex>.<base64_payload>" where:
- base64_payload = base64(serialize(data))
- hmac_hex = HMAC-SHA256(signing_key, base64_payload)
- signing_key = Crypto.derive_key(secret_key_base, "session_signing")
The payload is signed but not encrypted. This is safe as long as the session contains only non-secret identifiers (user IDs, CSRF tokens, flash messages). AES-256-GCM encryption will be added once the March runtime exposes the necessary builtins.
Cookie lifetime:
- Browser session (no Max-Age) by default.
- Persistent (Max-Age=N) when Session.persist/2 is called before commit.
Session auto-commit: Session.load/2 registers an after-send hook "session_commit" that writes the Set-Cookie header just before the response is flushed. Call Session.load/2 early in the middleware pipeline.
Typical pipeline:
conn
|> Session.load(secret_key_base)
|> CSRF.ensure_token()
|> CSRF.protect()
|> MyApp.Router.route()Reading and writing session values:
Session.get(conn, "user_id")
Session.put(conn, "user_id", "42")
Session.delete(conn, "user_id")
Session.clear(conn)Committing explicitly (if not using the after-send hook):
conn |> Session.commit(secret_key_base)Functions
Remove all keys from the session.
Session.clear(conn)Write the session back to a Set-Cookie header.
Called automatically by BastionServer via the "session_commit"
after-send hook registered by Session.load/2. You can also call
it explicitly before redirecting if the hook mechanism isn't available.
conn |> Session.commit(secret_key_base)Commit the session using the secret stored by Session.load/2.
Used by BastionServer's after-send hook dispatch — it has only the
conn and looks up the secret from assigns.Delete a key from the session.
Session.delete(conn, "user_id")Get a session value by key.
Session.get(conn, "user_id") -- Some("42") or NoneLoad the session from the request cookie and register the commit hook.
Decodes and verifies the "_session" cookie using `secret_key_base`.
If the cookie is absent or has been tampered with, starts a fresh
empty session.
Registers the "session_commit" after-send hook so the session is
automatically written back to a Set-Cookie header after the response.
Call this early in the request pipeline, after CSRF.ensure_token or
as the first middleware step.
conn |> Session.load(secret_key_base)Configure the session cookie to persist for seconds after creation.
Call before commit/2 or let the commit hook pick it up.
Session.persist(conn, 60 * 60 * 24 * 60) -- 60 daysStore a value in the session under key.
Marks the session as dirty so it will be committed.
Session.put(conn, "user_id", user_id)