From 3f621350837918b3e2463f060d4e330eb02c88c2 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Tue, 17 Oct 2023 17:25:48 +0200 Subject: [PATCH] Add validate_auth api endpoint (fixes #3702) (#4049) * Add validate_auth api endpoint (fixes #3702) * clippy --------- Co-authored-by: Dessalines --- crates/api/src/lib.rs | 22 +++++++++++++++++-- crates/api/src/local_user/mod.rs | 1 + crates/api/src/local_user/validate_auth.rs | 23 ++++++++++++++++++++ src/api_routes_http.rs | 4 +++- src/session_middleware.rs | 25 +++------------------- 5 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 crates/api/src/local_user/validate_auth.rs diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 300b89ffe..5621fe2df 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -2,11 +2,15 @@ use actix_web::{http::header::Header, HttpRequest}; use actix_web_httpauth::headers::authorization::{Authorization, Bearer}; use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine}; use captcha::Captcha; -use lemmy_api_common::utils::{local_site_to_slur_regex, AUTH_COOKIE_NAME}; +use lemmy_api_common::{ + claims::Claims, + context::LemmyContext, + utils::{check_user_valid, local_site_to_slur_regex, AUTH_COOKIE_NAME}, +}; use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::{ - error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}, + error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult}, utils::slurs::check_slurs, }; use std::io::Cursor; @@ -144,6 +148,20 @@ pub(crate) fn build_totp_2fa( .with_lemmy_type(LemmyErrorType::CouldntGenerateTotp) } +#[tracing::instrument(skip_all)] +pub async fn local_user_view_from_jwt( + jwt: &str, + context: &LemmyContext, +) -> Result { + let local_user_id = Claims::validate(jwt, context) + .await + .with_lemmy_type(LemmyErrorType::NotLoggedIn)?; + let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?; + check_user_valid(&local_user_view.person)?; + + Ok(local_user_view) +} + #[cfg(test)] mod tests { #![allow(clippy::unwrap_used)] diff --git a/crates/api/src/local_user/mod.rs b/crates/api/src/local_user/mod.rs index 1b58713f1..98e023fa5 100644 --- a/crates/api/src/local_user/mod.rs +++ b/crates/api/src/local_user/mod.rs @@ -14,4 +14,5 @@ pub mod report_count; pub mod reset_password; pub mod save_settings; pub mod update_totp; +pub mod validate_auth; pub mod verify_email; diff --git a/crates/api/src/local_user/validate_auth.rs b/crates/api/src/local_user/validate_auth.rs new file mode 100644 index 000000000..d95195dc9 --- /dev/null +++ b/crates/api/src/local_user/validate_auth.rs @@ -0,0 +1,23 @@ +use crate::{local_user_view_from_jwt, read_auth_token}; +use actix_web::{ + web::{Data, Json}, + HttpRequest, +}; +use lemmy_api_common::{context::LemmyContext, SuccessResponse}; +use lemmy_utils::error::{LemmyError, LemmyErrorType}; + +/// Returns an error message if the auth token is invalid for any reason. Necessary because other +/// endpoints silently treat any call with invalid auth as unauthenticated. +#[tracing::instrument(skip(context))] +pub async fn validate_auth( + req: HttpRequest, + context: Data, +) -> Result, LemmyError> { + let jwt = read_auth_token(&req)?; + if let Some(jwt) = jwt { + local_user_view_from_jwt(&jwt, &context).await?; + } else { + Err(LemmyErrorType::NotLoggedIn)?; + } + Ok(Json(SuccessResponse::default())) +} diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs index 3546b3400..26d6b9e8d 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_http.rs @@ -38,6 +38,7 @@ use lemmy_api::{ reset_password::reset_password, save_settings::save_user_settings, update_totp::update_totp, + validate_auth::validate_auth, verify_email::verify_email, }, post::{ @@ -296,7 +297,8 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { .route("/leave_admin", web::post().to(leave_admin)) .route("/totp/generate", web::post().to(generate_totp_secret)) .route("/totp/update", web::post().to(update_totp)) - .route("/list_logins", web::get().to(list_logins)), + .route("/list_logins", web::get().to(list_logins)) + .route("/validate_auth", web::get().to(validate_auth)), ) .service( web::scope("/user") diff --git a/src/session_middleware.rs b/src/session_middleware.rs index 80b79f917..ae82cd44d 100644 --- a/src/session_middleware.rs +++ b/src/session_middleware.rs @@ -7,14 +7,8 @@ use actix_web::{ }; use core::future::Ready; use futures_util::future::LocalBoxFuture; -use lemmy_api::read_auth_token; -use lemmy_api_common::{ - claims::Claims, - context::LemmyContext, - lemmy_db_views::structs::LocalUserView, - utils::check_user_valid, -}; -use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType}; +use lemmy_api::{local_user_view_from_jwt, read_auth_token}; +use lemmy_api_common::context::LemmyContext; use reqwest::header::HeaderValue; use std::{future::ready, rc::Rc}; @@ -100,20 +94,6 @@ where } } -#[tracing::instrument(skip_all)] -async fn local_user_view_from_jwt( - jwt: &str, - context: &LemmyContext, -) -> Result { - let local_user_id = Claims::validate(jwt, context) - .await - .with_lemmy_type(LemmyErrorType::NotLoggedIn)?; - let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?; - check_user_valid(&local_user_view.person)?; - - Ok(local_user_view) -} - #[cfg(test)] mod tests { #![allow(clippy::unwrap_used)] @@ -121,6 +101,7 @@ mod tests { use super::*; use actix_web::test::TestRequest; + use lemmy_api_common::claims::Claims; use lemmy_db_schema::{ source::{ instance::Instance,