diff --git a/geneit_backend/src/controllers/auth_controller.rs b/geneit_backend/src/controllers/auth_controller.rs index 9c2e999..0455147 100644 --- a/geneit_backend/src/controllers/auth_controller.rs +++ b/geneit_backend/src/controllers/auth_controller.rs @@ -1,6 +1,7 @@ use crate::constants::StaticConstraints; use crate::controllers::HttpResult; use crate::models::{User, UserID}; +use crate::services::login_token_service::LoginTokenValue; use crate::services::rate_limiter_service::RatedAction; use crate::services::{login_token_service, openid_service, rate_limiter_service, users_service}; use actix_remote_ip::RemoteIP; @@ -304,3 +305,10 @@ pub async fn finish_openid_login( finish_login(&user).await } + +/// Logout user +pub async fn logout(token: LoginTokenValue) -> HttpResult { + login_token_service::delete_token(&token).await?; + + Ok(HttpResponse::NoContent().finish()) +} diff --git a/geneit_backend/src/main.rs b/geneit_backend/src/main.rs index b8e2912..dc83c0d 100644 --- a/geneit_backend/src/main.rs +++ b/geneit_backend/src/main.rs @@ -51,6 +51,7 @@ async fn main() -> std::io::Result<()> { "/auth/finish_openid_login", web::post().to(auth_controller::finish_openid_login), ) + .route("/auth/logout", web::get().to(auth_controller::logout)) // User controller .route("/user/info", web::get().to(user_controller::auth_info)) }) diff --git a/geneit_backend/src/services/login_token_service.rs b/geneit_backend/src/services/login_token_service.rs index f34bd32..89ba882 100644 --- a/geneit_backend/src/services/login_token_service.rs +++ b/geneit_backend/src/services/login_token_service.rs @@ -6,12 +6,19 @@ use crate::utils::string_utils; use crate::utils::time_utils::time; use actix_web::dev::Payload; use actix_web::{FromRequest, HttpRequest}; +use std::future::{ready, Ready}; use std::time::Duration; +#[derive(Debug, Clone)] +pub struct LoginTokenValue(String); + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct LoginToken { + #[serde(rename = "e")] expire: u64, + #[serde(rename = "h")] hb: u64, + #[serde(rename = "u")] pub user_id: UserID, } @@ -64,6 +71,12 @@ pub async fn gen_new_token(user: &User) -> anyhow::Result { Ok(key) } +/// Delete a login token +pub async fn delete_token(token: &LoginTokenValue) -> anyhow::Result<()> { + redis_connection::remove_value(&redis_key(&token.0)).await?; + Ok(()) +} + /// Get a user information from its token async fn get_token(key: &str) -> anyhow::Result> { let token = match redis_connection::get_value::(&redis_key(key)).await? { @@ -87,6 +100,20 @@ async fn get_token(key: &str) -> anyhow::Result> { Ok(Some(token)) } +impl FromRequest for LoginTokenValue { + type Error = actix_web::Error; + type Future = Ready>; + + fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future { + ready(match req.headers().get("X-Auth-Token") { + Some(v) => Ok(LoginTokenValue(v.to_str().unwrap_or("").to_string())), + None => Err(actix_web::error::ErrorBadRequest( + "Missing auth token header!", + )), + }) + } +} + impl FromRequest for LoginToken { type Error = actix_web::Error; type Future = futures_util::future::LocalBoxFuture<'static, Result>; @@ -94,16 +121,9 @@ impl FromRequest for LoginToken { fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future { let req = req.clone(); Box::pin(async move { - let token = match req.headers().get("X-Auth-Token") { - Some(v) => v.to_str().unwrap_or(""), - None => { - return Err(actix_web::error::ErrorBadRequest( - "Missing auth token header!", - )); - } - }; + let token = LoginTokenValue::extract(&req).await?; - let token = match get_token(token).await { + let token = match get_token(&token.0).await { Err(e) => { log::error!("Failed to load auth token! {}", e); return Err(actix_web::error::ErrorInternalServerError(