Cookbook / Auth and sessions
Sessions and broadcast on Redis
Share sessions and live updates across every instance with axeon-redis.
MemoryStore keeps sessions in one process, so they vanish on a restart and do not follow a user between instances. axeon-redis stores them in Redis instead, and also provides a backend for broadcasting messages across instances. It needs Redis 6.2 or newer.
cargo add axeon-redis
use axeon::broadcast::Hub;
use axeon::cookies::{CookieManager, Key};
use axeon::session::Sessions;
use axeon::{Server, ServerError};
use axeon_redis::{RedisBroadcast, RedisSessionStore};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".into());
// One multiplexed connection that reconnects by itself.
let redis = axeon_redis::connect(&url).await?;
let mut app = Server::new();
app.middleware(CookieManager::new().key(Key::generate()))
.middleware(
Sessions::new(RedisSessionStore::new(redis.clone()).prefix("myapp:session:"))
.ttl(Duration::from_secs(24 * 3600))
.secure(!cfg!(debug_assertions)),
);
// Live updates that reach clients on every instance.
app.state(Hub::new(RedisBroadcast::connect(&url).await?.max_len(1000)));
app.post("/visit", |req| async move {
let session = req.session()?;
let visits: u64 = session.get("visits").unwrap_or(0) + 1;
session.insert("visits", visits)?;
Ok::<_, ServerError>(format!("visit {}", visits))
});
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
app.serve_with_shutdown(listener, axeon::shutdown_signal()).await?;
Ok(())
}
docker run --rm -p 6379:6379 redis:7
curl -c jar -b jar -X POST http://127.0.0.1:3000/visit # visit 1
curl -c jar -b jar -X POST http://127.0.0.1:3000/visit # visit 2, even after a restart
Notes#
- Each session is one key holding JSON, expiring with the session's TTL. Keys are the prefix plus a SHA-256 hash of the session ID, so anyone who can read Redis cannot turn a key back into a usable cookie.
- While Redis is down, requests that use a session fail immediately with
500rather than waiting; other routes are unaffected. When Redis returns, requests work again with no restart. - Add the
tlsfeature forrediss://URLs, as managed Redis services require. - The
rediscrate is re-exported asaxeon_redis::redisfor caching and other commands. - This recipe has no CSRF middleware to stay short. Browser apps need it: see Cookies, sessions and CSRF.