Enforce 30-day session expiry with lazy cleanup

Sessions never expired server-side despite the 30-day cookie Max-Age;
repeat sign-ins orphaned rows. user_id now rejects tokens older than 30
days and deletes them on sight. Verified live: fresh 200, backdated 302
with row removed.
This commit is contained in:
dev 2026-10-13 00:20:58 +05:30
parent 0f2a9743f2
commit 39332670be

View file

@ -100,17 +100,32 @@ pub async fn user_id(pool: &SqlitePool, h: &HeaderMap) -> Option<String> {
}
let token = token?;
tracing::debug!("session token={token}");
match sqlx::query_scalar::<_, String>("SELECT user_id FROM sessions WHERE token=?")
// Sessions live 30 days (matching the cookie Max-Age). Stale rows are
// deleted on sight so repeat sign-ins don't orphan tokens forever.
let row: Option<(String, String)> =
sqlx::query_as("SELECT user_id, created_at FROM sessions WHERE token=?")
.bind(token)
.fetch_optional(pool)
.await
{
Ok(found) => {
tracing::debug!("session lookup -> {:?}", found);
found
.unwrap_or(None);
match row {
Some((uid, created)) => {
let fresh = chrono::DateTime::parse_from_rfc3339(&created)
.map(|t| (chrono::Utc::now() - t.with_timezone(&chrono::Utc)).num_days() < 30)
.unwrap_or(false);
if !fresh {
sqlx::query("DELETE FROM sessions WHERE token=?")
.bind(token)
.execute(pool)
.await
.ok();
return None;
}
Err(e) => {
tracing::error!("session lookup failed: {e}");
tracing::debug!("session lookup -> {:?}", uid);
Some(uid)
}
None => {
tracing::debug!("session lookup -> None");
None
}
}