| 1 |
|
| 2 |
|
| 3 |
use axum::{ |
| 4 |
extract::{Path, State}, |
| 5 |
response::IntoResponse, |
| 6 |
}; |
| 7 |
use tower_sessions::Session; |
| 8 |
|
| 9 |
use crate::{ |
| 10 |
auth::{AuthUser, SESSION_TRACKING_KEY}, |
| 11 |
db::{self, UserSessionId}, |
| 12 |
error::{AppError, Result}, |
| 13 |
templates::UserSessionsPartialTemplate, |
| 14 |
AppState, |
| 15 |
}; |
| 16 |
|
| 17 |
|
| 18 |
#[tracing::instrument(skip_all, name = "api::revoke_session")] |
| 19 |
pub(in crate::routes::api) async fn revoke_session( |
| 20 |
State(state): State<AppState>, |
| 21 |
session: Session, |
| 22 |
AuthUser(user): AuthUser, |
| 23 |
Path(session_id): Path<UserSessionId>, |
| 24 |
) -> Result<impl IntoResponse> { |
| 25 |
|
| 26 |
if let Ok(Some(current_id)) = session.get::<UserSessionId>(SESSION_TRACKING_KEY).await |
| 27 |
&& current_id == session_id |
| 28 |
{ |
| 29 |
return Err(AppError::BadRequest( |
| 30 |
"Use logout to end your current session".to_string(), |
| 31 |
)); |
| 32 |
} |
| 33 |
|
| 34 |
db::sessions::delete_user_session(&state.db, session_id, user.id).await?; |
| 35 |
state.session_cache.remove(&session_id); |
| 36 |
|
| 37 |
|
| 38 |
let sessions = db::sessions::get_user_sessions(&state.db, user.id).await?; |
| 39 |
let current_tracking_id = session |
| 40 |
.get::<UserSessionId>(SESSION_TRACKING_KEY) |
| 41 |
.await |
| 42 |
.ok() |
| 43 |
.flatten(); |
| 44 |
|
| 45 |
Ok(UserSessionsPartialTemplate { |
| 46 |
sessions, |
| 47 |
current_session_id: current_tracking_id, |
| 48 |
}) |
| 49 |
} |
| 50 |
|
| 51 |
|
| 52 |
#[tracing::instrument(skip_all, name = "api::revoke_other_sessions")] |
| 53 |
pub(in crate::routes::api) async fn revoke_other_sessions( |
| 54 |
State(state): State<AppState>, |
| 55 |
session: Session, |
| 56 |
AuthUser(user): AuthUser, |
| 57 |
) -> Result<impl IntoResponse> { |
| 58 |
let current_tracking_id = session |
| 59 |
.get::<UserSessionId>(SESSION_TRACKING_KEY) |
| 60 |
.await |
| 61 |
.ok() |
| 62 |
.flatten(); |
| 63 |
|
| 64 |
if let Some(current_id) = current_tracking_id { |
| 65 |
let revoked_ids = db::sessions::delete_other_sessions(&state.db, current_id, user.id).await?; |
| 66 |
for id in &revoked_ids { |
| 67 |
state.session_cache.remove(id); |
| 68 |
} |
| 69 |
tracing::info!(user_id = %user.id, revoked = revoked_ids.len(), event = "revoke_other_sessions", "Revoked other sessions"); |
| 70 |
} |
| 71 |
|
| 72 |
|
| 73 |
let sessions = db::sessions::get_user_sessions(&state.db, user.id).await?; |
| 74 |
|
| 75 |
Ok(UserSessionsPartialTemplate { |
| 76 |
sessions, |
| 77 |
current_session_id: current_tracking_id, |
| 78 |
}) |
| 79 |
} |
| 80 |
|