From 7262a06f28504dfd31f283e84f0df1ef62de66a5 Mon Sep 17 00:00:00 2001 From: Doloro1978 Date: Wed, 15 Jul 2026 14:41:54 +0100 Subject: [PATCH] qol(http): replaced all explicit header setting with axum cookie jar --- Cargo.lock | 34 +++++++++++++++++++++++++++ crates/server/Cargo.toml | 1 + crates/server/src/http.rs | 49 +++++++++------------------------------ 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89179e5..90b75bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,6 +366,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-extra" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be44683b41ccb9ab2d23a5230015c9c3c55be97a25e4428366de8873103f7970" +dependencies = [ + "axum", + "axum-core", + "bytes", + "cookie", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -682,6 +704,17 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -2871,6 +2904,7 @@ dependencies = [ "argon2", "async-broadcast", "axum", + "axum-extra", "bytes", "chrono", "dashmap", diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index 6e6bcc7..702a34c 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -37,3 +37,4 @@ rubato = "3.0.0" chrono = "0.4.45" regex = "1" thiserror = "2.0.18" +axum-extra = { version = "0.12.6", features = ["cookie"] } diff --git a/crates/server/src/http.rs b/crates/server/src/http.rs index 4491284..c5f6097 100644 --- a/crates/server/src/http.rs +++ b/crates/server/src/http.rs @@ -6,6 +6,7 @@ use std::{ }, }; +use axum_extra::extract::{CookieJar, cookie::Cookie}; use regex::Regex; // The Rust `regex` crate is guaranteed linear-time and therefore does NOT @@ -206,22 +207,6 @@ struct CreateStreamKeyBody { label: String, } -fn extract_session_token(headers: &HeaderMap) -> Option { - // Check bare `session` header first (curl / API clients) - if let Some(v) = headers.get("session") { - return Some(v.to_str().ok()?.to_string()); - } - // Fall back to Cookie header (browsers) - let cookie_header = headers.get("cookie")?.to_str().ok()?; - cookie_header - .split(';') - .find_map(|pair| { - let pair = pair.trim(); - pair.strip_prefix("session=") - }) - .map(|v| v.to_string()) -} - struct AuthUser(entity::users::Model); impl FromRequestParts> for AuthUser { @@ -231,9 +216,10 @@ impl FromRequestParts> for AuthUser { parts: &mut Parts, state: &Arc, ) -> Result { - let token = extract_session_token(&parts.headers).ok_or(HttpError::Unauthorized)?; + let jar = CookieJar::from_request_parts(parts, state).await.unwrap(); + let session = jar.get("session").ok_or(HttpError::Unauthorized)?; - let user = users::Entity::find_by_auth_session(&state.db, token) + let user = users::Entity::find_by_auth_session(&state.db, session.to_string()) .await? .ok_or(HttpError::Unauthorized)?; @@ -306,6 +292,7 @@ struct LoginResponse { async fn login_handler( State(state): State>, Json(payload): Json, + jar: CookieJar, ) -> Result { let user = users::Entity::find_by_username(&state.db, payload.username.clone()) .await? @@ -323,16 +310,8 @@ async fn login_handler( let auth = auth_session::Entity::create(&state.db, user.id).await?; let token = auth.value; - let mut meow = Response::new("".to_string()); - // TODO: Replace with axum cookie jar https://docs.rs/axum-extra/latest/axum_extra/extract/cookie/struct.CookieJar.html - meow.headers_mut().insert( - SET_COOKIE, - format!("session={token}; SameSite=Strict; Path=/; Max-Age=2592000") - .parse() - .unwrap(), - ); - *meow.status_mut() = StatusCode::OK; - Ok(meow) + let jar = jar.add(Cookie::new("session", token)); + Ok((StatusCode::OK, jar)) } #[derive(Deserialize)] @@ -345,7 +324,8 @@ struct CreateUserForm { async fn create_user_handler( State(state): State>, Json(payload): Json, -) -> Result<(HeaderMap, StatusCode), HttpError> { + jar: CookieJar, +) -> Result { if state.config.signup_code.is_empty() || payload.ref_token != state.config.signup_code { warn!(username = %payload.username, "signup rejected: invalid signup code"); return Err(HttpError::Unauthorized); @@ -372,15 +352,8 @@ async fn create_user_handler( let session = auth_session::Entity::create(&state.db, user.id).await?; let token = session.value; - let mut headers = HeaderMap::new(); - // TODO: Replace with axum cookie jar https://docs.rs/axum-extra/latest/axum_extra/extract/cookie/struct.CookieJar.html - headers.insert( - SET_COOKIE, - format!("session={token}; HttpOnly; SameSite=Strict; Path=/") - .parse() - .unwrap(), - ); - Ok((headers, StatusCode::OK)) + let jar = jar.add(Cookie::new("session", token)); + Ok((jar, StatusCode::OK)) } async fn stream_handler(