diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-09-04 01:25:31 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-09-04 01:25:54 -0400 |
| commit | 072dfa9a0bae5b7e9ea1caa97f6a90bd576a5d95 (patch) | |
| tree | 3194c56bbf1b9729d07198973815c0cb88a9e5c6 /src/login | |
| parent | 2965a788cfcf4a0386cb8832e0d96491bf54c1d3 (diff) | |
Expire sessions after 90 days.
Diffstat (limited to 'src/login')
| -rw-r--r-- | src/login/extract/login.rs | 3 | ||||
| -rw-r--r-- | src/login/repo/tokens.rs | 18 | ||||
| -rw-r--r-- | src/login/routes.rs | 6 |
3 files changed, 23 insertions, 4 deletions
diff --git a/src/login/extract/login.rs b/src/login/extract/login.rs index f49933a..ce820f1 100644 --- a/src/login/extract/login.rs +++ b/src/login/extract/login.rs @@ -6,6 +6,7 @@ use axum::{ use sqlx::sqlite::SqlitePool; use crate::{ + clock::RequestedAt, error::InternalError, login::{ extract::IdentityToken, @@ -22,11 +23,13 @@ impl FromRequestParts<SqlitePool> for Login { state: &SqlitePool, ) -> Result<Self, Self::Rejection> { let identity_token = IdentityToken::from_request_parts(parts, state).await?; + let requested_at = RequestedAt::from_request_parts(parts, state).await?; let token = identity_token.token().ok_or(LoginError::Forbidden)?; let db = State::<SqlitePool>::from_request_parts(parts, state).await?; let mut tx = db.begin().await?; + tx.tokens().expire(requested_at.timestamp()).await?; let login = tx.tokens().validate(token).await?; tx.commit().await?; diff --git a/src/login/repo/tokens.rs b/src/login/repo/tokens.rs index 584f6dc..3ec3d63 100644 --- a/src/login/repo/tokens.rs +++ b/src/login/repo/tokens.rs @@ -1,3 +1,4 @@ +use chrono::TimeDelta; use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction}; use uuid::Uuid; @@ -62,6 +63,23 @@ impl<'c> Tokens<'c> { Ok(()) } + pub async fn expire(&mut self, expire_at: DateTime) -> Result<(), BoxedError> { + // Somewhat arbitrarily, expire after 90 days. + let expired_issue_at = expire_at - TimeDelta::days(90); + sqlx::query!( + r#" + delete + from token + where issued_at < $1 + "#, + expired_issue_at, + ) + .execute(&mut *self.0) + .await?; + + Ok(()) + } + /// Validate a token by its secret, retrieving the associated Login record. /// Will return [None] if the token is not valid. pub async fn validate(&mut self, secret: &str) -> Result<Option<Login>, BoxedError> { diff --git a/src/login/routes.rs b/src/login/routes.rs index a00982d..2269ea6 100644 --- a/src/login/routes.rs +++ b/src/login/routes.rs @@ -5,10 +5,9 @@ use axum::{ routing::post, Router, }; -use chrono::Utc; use sqlx::sqlite::SqlitePool; -use crate::error::InternalError; +use crate::{clock::RequestedAt, error::InternalError}; use super::{ extract::IdentityToken, @@ -29,11 +28,10 @@ struct Login { async fn on_login( State(db): State<SqlitePool>, + RequestedAt(now): RequestedAt, identity: IdentityToken, Form(form): Form<Login>, ) -> Result<impl IntoResponse, InternalError> { - let now = Utc::now(); - if identity.token().is_some() { return Ok((StatusCode::BAD_REQUEST, identity, "already logged in")); } |
