|
|
@@ -153,7 +153,7 @@ pub struct NewSession {
|
|
|
pub csrf_token: SecretString,
|
|
|
}
|
|
|
|
|
|
-/// Cloneable server-side session store with TTL expiry.
|
|
|
+/// Cloneable server-side session store with sliding TTL expiry.
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct SessionStore {
|
|
|
inner: Arc<RwLock<HashMap<String, SessionEntry>>>,
|
|
|
@@ -167,7 +167,7 @@ struct SessionEntry {
|
|
|
}
|
|
|
|
|
|
impl SessionStore {
|
|
|
- /// Create an empty store; entries expire `ttl` after creation.
|
|
|
+ /// Create an empty store; entries expire `ttl` after last activity.
|
|
|
#[must_use]
|
|
|
pub fn new(ttl: Duration) -> Self {
|
|
|
Self {
|
|
|
@@ -198,14 +198,15 @@ impl SessionStore {
|
|
|
}
|
|
|
|
|
|
/// Look up a session; expired or unknown ids return `None` (expired entries
|
|
|
- /// are evicted).
|
|
|
+ /// are evicted). A hit slides the expiry forward to now.
|
|
|
pub fn get(&self, id: &str) -> Option<SessionData> {
|
|
|
let mut guard = self.inner.write().ok()?;
|
|
|
- let entry = guard.get(id)?;
|
|
|
+ let entry = guard.get_mut(id)?;
|
|
|
if entry.created_at.elapsed() >= self.ttl {
|
|
|
guard.remove(id);
|
|
|
return None;
|
|
|
}
|
|
|
+ entry.created_at = Instant::now();
|
|
|
Some(entry.data.clone())
|
|
|
}
|
|
|
|
|
|
@@ -219,13 +220,14 @@ impl SessionStore {
|
|
|
/// [`SessionError::Lock`] when the store lock is poisoned.
|
|
|
pub fn rotate(&self, old_id: &str) -> Result<Option<String>, SessionError> {
|
|
|
let mut guard = self.inner.write().map_err(|_| SessionError::Lock)?;
|
|
|
- let Some(entry) = guard.remove(old_id) else {
|
|
|
+ let Some(mut entry) = guard.remove(old_id) else {
|
|
|
return Ok(None);
|
|
|
};
|
|
|
if entry.created_at.elapsed() >= self.ttl {
|
|
|
return Ok(None);
|
|
|
}
|
|
|
let next_id = new_token()?;
|
|
|
+ entry.created_at = Instant::now();
|
|
|
guard.insert(next_id.clone(), entry);
|
|
|
Ok(Some(next_id))
|
|
|
}
|
|
|
@@ -258,6 +260,7 @@ impl SessionStore {
|
|
|
}
|
|
|
entry.data.set_tokens(access_token, refresh_token);
|
|
|
entry.data.set_token_type(token_type);
|
|
|
+ entry.created_at = Instant::now();
|
|
|
true
|
|
|
}
|
|
|
|