From d3afac6aaee34ff3adcdab4233eb5d6c18f18524 Mon Sep 17 00:00:00 2001 From: Celeo Date: Mon, 21 Oct 2024 17:31:50 -0700 Subject: [PATCH] Make cookie sessions longer --- Cargo.lock | 1 + vzdv-site/Cargo.toml | 1 + vzdv-site/src/main.rs | 13 +++++++++---- vzdv-site/src/middleware.rs | 18 ++++++++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09edaef..5147d36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3683,6 +3683,7 @@ dependencies = [ "sqlx", "thiserror", "thousands", + "time", "tokio", "tower", "tower-http", diff --git a/vzdv-site/Cargo.toml b/vzdv-site/Cargo.toml index 17628f8..11f87e3 100644 --- a/vzdv-site/Cargo.toml +++ b/vzdv-site/Cargo.toml @@ -32,6 +32,7 @@ serde_json = "1.0.113" sqlx = { version = "0.8.1", default-features = false, features = ["runtime-tokio", "sqlx-sqlite", "chrono"] } thousands = "0.2.0" thiserror = "1.0.63" +time = { version = "0.3.36", default-features = false } tokio = { version = "1.36.0", features = ["full"] } tower = "0.4.13" tower-http = { version = "0.5.2", features = ["fs", "timeout"] } diff --git a/vzdv-site/src/main.rs b/vzdv-site/src/main.rs index ad9aa81..381f383 100644 --- a/vzdv-site/src/main.rs +++ b/vzdv-site/src/main.rs @@ -19,7 +19,7 @@ use std::{ use tokio::signal; use tower::ServiceBuilder; use tower_http::timeout::TimeoutLayer; -use tower_sessions::SessionManagerLayer; +use tower_sessions::{Expiry, SessionManagerLayer}; use tower_sessions_sqlx_store::SqliteStore; use vzdv::general_setup; @@ -81,7 +81,8 @@ fn load_router( ServiceBuilder::new() .layer(TimeoutLayer::new(Duration::from_secs(30))) .layer(axum_middleware::from_fn(middleware::logging)) - .layer(sessions_layer), + .layer(sessions_layer) + .layer(axum_middleware::from_fn(middleware::extend_session)), ) .fallback(endpoints::page_404) } @@ -127,9 +128,13 @@ async fn main() { error!("Could not create table for sessions: {e}"); return; } + // "lax" seems to be needed for the Discord OAuth login, but is there a concern about security? - let session_layer = - SessionManagerLayer::new(sessions).with_same_site(tower_sessions::cookie::SameSite::Lax); + let session_layer = SessionManagerLayer::new(sessions) + .with_same_site(tower_sessions::cookie::SameSite::Lax) + .with_expiry(Expiry::OnInactivity(time::Duration::hours( + middleware::SESSION_INACTIVITY_WINDOW, + ))); let mut templates = match load_templates() { Ok(t) => t, Err(e) => { diff --git a/vzdv-site/src/middleware.rs b/vzdv-site/src/middleware.rs index e6fa6e5..024e3cd 100644 --- a/vzdv-site/src/middleware.rs +++ b/vzdv-site/src/middleware.rs @@ -3,9 +3,13 @@ use axum::{extract::Request, middleware::Next, response::Response}; use log::{debug, warn}; use std::{collections::HashSet, sync::LazyLock}; +use tower_sessions::{Expiry, Session}; static IGNORE_PATHS: LazyLock> = LazyLock::new(|| HashSet::from(["/favicon.ico"])); +/// Cookie expiration duration (hours). +pub const SESSION_INACTIVITY_WINDOW: i64 = 24; + /// Simple logging middleware. /// /// Logs the method, path, and response code to debug @@ -28,3 +32,17 @@ pub async fn logging(request: Request, next: Next) -> Response { next.run(request).await } } + +/// Middleware to extend the `tower_sessions` session cookie on the user's browser. +/// +/// The cookie's initial duration covers how long the cookie will last between +/// site visits, as this middleware extends the duration of the cookie by the +/// same amount each time the user visits any page on the site. +/// +/// This does touch the DB, which I don't love. +pub async fn extend_session(session: Session, request: Request, next: Next) -> Response { + session.set_expiry(Some(Expiry::OnInactivity(time::Duration::hours( + SESSION_INACTIVITY_WINDOW, + )))); + next.run(request).await +}