//! Session management: revoke individual or other sessions. use axum::{ extract::{Path, State}, response::IntoResponse, }; use tower_sessions::Session; use crate::{ auth::{AuthUser, SESSION_TRACKING_KEY}, db::{self, UserSessionId}, error::{AppError, Result}, templates::UserSessionsPartialTemplate, AppState, }; /// Revoke a single session (sign out another device). #[tracing::instrument(skip_all, name = "api::revoke_session")] pub(in crate::routes::api) async fn revoke_session( State(state): State, session: Session, AuthUser(user): AuthUser, Path(session_id): Path, ) -> Result { // Don't allow revoking your own current session via this endpoint if let Ok(Some(current_id)) = session.get::(SESSION_TRACKING_KEY).await && current_id == session_id { return Err(AppError::BadRequest( "Use logout to end your current session".to_string(), )); } db::sessions::delete_user_session(&state.db, session_id, user.id).await?; state.session_cache.remove(&session_id); // Re-render the sessions list let sessions = db::sessions::get_user_sessions(&state.db, user.id).await?; let current_tracking_id = session .get::(SESSION_TRACKING_KEY) .await .ok() .flatten(); Ok(UserSessionsPartialTemplate { sessions, current_session_id: current_tracking_id, }) } /// Revoke all sessions except the current one. #[tracing::instrument(skip_all, name = "api::revoke_other_sessions")] pub(in crate::routes::api) async fn revoke_other_sessions( State(state): State, session: Session, AuthUser(user): AuthUser, ) -> Result { let current_tracking_id = session .get::(SESSION_TRACKING_KEY) .await .ok() .flatten(); if let Some(current_id) = current_tracking_id { let revoked_ids = db::sessions::delete_other_sessions(&state.db, current_id, user.id).await?; for id in &revoked_ids { state.session_cache.remove(id); } tracing::info!(user_id = %user.id, revoked = revoked_ids.len(), event = "revoke_other_sessions", "Revoked other sessions"); } // Re-render the sessions list let sessions = db::sessions::get_user_sessions(&state.db, user.id).await?; Ok(UserSessionsPartialTemplate { sessions, current_session_id: current_tracking_id, }) }