From d4dbd1bda5cfde5d52fbcf7eba3fc048c10dd0b2 Mon Sep 17 00:00:00 2001 From: Julio Merino Date: Sun, 3 Sep 2023 07:22:51 -0700 Subject: [PATCH] Invalidate cached sessions on logout This is not strictly necessary and it won't do the right thing when running in Azure Functions (because we don't know how many frontends the runtime will spawn and we cannot reliably do cache invalidation across them all), but it's still the right thing to do. After all, consumers of this code could run in a single server, and in that case we would want this to happen. --- authn/src/driver/logout.rs | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/authn/src/driver/logout.rs b/authn/src/driver/logout.rs index c215fc7..ecf9234 100644 --- a/authn/src/driver/logout.rs +++ b/authn/src/driver/logout.rs @@ -34,6 +34,15 @@ impl AuthnDriver { db::delete_session(tx.ex(), session, now).await?; tx.commit().await?; + + // Removing the session from the cache is only a best-effort operation. If we end up with + // multiple instances of a frontend running at once, there is no easy way to perform cache + // invalidation across all of them. But we don't know how this code is consumed (maybe it + // is part of a single-instance server instead of a lambda-style deployment), so let's try + // to do the right thing. + let mut cache = self.sessions_cache.lock().await; + let _previous = cache.remove(&token); + Ok(()) } } @@ -44,6 +53,7 @@ mod tests { use crate::driver::testutils::*; use crate::driver::AuthnOptions; use iii_iv_core::db::DbError; + use std::time::Duration; #[tokio::test] async fn test_ok() { @@ -88,4 +98,33 @@ mod tests { assert_eq!(err1, err2); } + + #[tokio::test] + async fn test_remove_from_sessions_cache() { + // Configure a cache with just one entry and "infinite" duration so that we can precisely + // control when entries get evicted. + let opts = AuthnOptions { + sessions_cache_capacity: 1, + sessions_cache_ttl: Duration::from_secs(900), + ..Default::default() + }; + let context = TestContext::setup(opts).await; + + let username = Username::from("test"); + + assert_eq!(0, context.driver().sessions_cache.lock().await.len()); + let token = context.do_test_login(username.clone()).await; + + let mut tx = context.db().begin().await.unwrap(); + let _user = context + .driver() + .get_session(&mut tx, context.driver().now_utc(), token.clone()) + .await + .unwrap(); + tx.commit().await.unwrap(); + assert_eq!(1, context.driver().sessions_cache.lock().await.len()); + + context.driver().logout(token.clone(), username).await.unwrap(); + assert_eq!(0, context.driver().sessions_cache.lock().await.len()); + } }