use axum::{ extract::{Extension, State}, http::{HeaderMap, StatusCode}, response::IntoResponse, }; use module_auth::LogoutCurrentSessionInput; use platform_auth::hash_refresh_session_token; use shared_contracts::auth::LogoutResponse; use time::OffsetDateTime; use crate::{ api_response::json_success_body, auth::{AuthenticatedAccessToken, RefreshSessionToken}, auth_session::{ attach_set_cookie_header, build_clear_refresh_session_cookie_header, map_logout_error, }, http_error::AppError, request_context::RequestContext, state::AppState, }; pub async fn logout( State(state): State, Extension(request_context): Extension, Extension(authenticated): Extension, maybe_refresh_token: Option>, ) -> Result { let refresh_token_hash = maybe_refresh_token.and_then(|token| { let token = token.0.token().trim().to_string(); if token.is_empty() { return None; } Some(hash_refresh_session_token(&token)) }); state .auth_user_service() .logout_current_session( LogoutCurrentSessionInput { user_id: authenticated.claims().user_id().to_string(), refresh_token_hash, }, OffsetDateTime::now_utc(), ) .map_err(map_logout_error)?; state .sync_auth_store_snapshot_to_spacetime() .await .map_err(|error| { AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR) .with_message(format!("同步认证快照失败:{error}")) })?; let mut headers = HeaderMap::new(); attach_set_cookie_header( &mut headers, build_clear_refresh_session_cookie_header(&state)?, ); Ok(( headers, json_success_body(Some(&request_context), LogoutResponse { ok: true }), )) }