Hello folks!
I am building a small server with axum-login. I replaced the MemoryStore by a RedisStore, following the example in tower-sessions-stores/redis-store.
I was expecting sessions to survive a server restart, but they do not. Is that expected or am I missing something?
Inspecting redis, I can see that the cookie is persisted when the server is down. Yet when I run it again, as soon as I run a query the cookie is being removed by axum-login.
I did not manage to decode the redis key but it seems some parts are changing (not only the argon2 sha).
Shouldn't sessions be persisted over server restart?
Here is my code, summarized:
Code
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
let redis_pool =
Pool::new(Config::default(), None, None, None, 6).expect("could not create Redis pool");
let redis_conn = redis_pool.connect();
redis_pool.wait_for_connect().await?;
let session_store = RedisStore::new(redis_pool);
let session_layer = SessionManagerLayer::new(session_store)
.with_expiry(Expiry::OnInactivity(Duration::days(1)))
.with_secure(false);
let backend = users::Backend::new();
let auth_layer = AuthManagerLayerBuilder::new(backend, session_layer).build();
let app = Router::new()
.route(
"/login",
get(auth::get_login)
.route_layer(login_required!(users::Backend))
.delete(auth::logout)
.route_layer(login_required!(users::Backend))
.post(auth::post_login),
)
.layer(auth_layer);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
redis_conn.await??;
Ok(())
}
and also:
impl AuthnBackend for Backend {
type User = User;
type Credentials = Credentials;
type Error = Error;
async fn authenticate(
&self,
creds: Self::Credentials,
) -> Result<Option<Self::User>, Self::Error> {
println!("authenticate");
let user: Option<Self::User> = self.users.get(&creds.email).cloned();
// Verifying the password is blocking and potentially slow, so we'll do so via
// `spawn_blocking`.
task::spawn_blocking(|| {
// We're using password-based authentication--this works by comparing our form
// input with an argon2 password hash.
Ok(user.filter(|user| verify_password(creds.password, &user.password).is_ok()))
})
.await?
}
async fn get_user(&self, email: &UserId<Self>) -> Result<Option<Self::User>, Self::Error> {
Ok(self.users.get(email).cloned())
}
}