From 4ae7b3813188b1f328bcc87b3c5c39b52664b4a4 Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 12 Jun 2026 17:02:22 +0800 Subject: [PATCH] feat(auth): add Secure flag to session cookie via COOKIE_SECURE env --- src/api/auth.rs | 13 +++++-------- src/auth/session.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/api/auth.rs b/src/api/auth.rs index a914eef..d2e3c9f 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -229,10 +229,7 @@ pub async fn login(username: String, password: String) -> Result Result { let client = get_conn().await.map_err(AppError::db_conn)?; + let cookie = session::session_cookie("", 0, session::cookie_secure()); if let Some(ctx) = dioxus::fullstack::FullstackContext::current() { - ctx.add_response_header( - SET_COOKIE, - HeaderValue::from_static("session=; HttpOnly; Path=/; Max-Age=0; SameSite=Lax"), - ); + if let Ok(value) = HeaderValue::try_from(cookie.as_str()) { + ctx.add_response_header(SET_COOKIE, value); + } } if let Some(t) = token { diff --git a/src/auth/session.rs b/src/auth/session.rs index ffed5e7..582bba1 100644 --- a/src/auth/session.rs +++ b/src/auth/session.rs @@ -19,6 +19,22 @@ pub fn default_expiry() -> DateTime { Utc::now() + Duration::days(30) } +#[cfg(feature = "server")] +pub fn cookie_secure() -> bool { + std::env::var("COOKIE_SECURE") + .ok() + .map(|v| matches!(v.as_str(), "1" | "true" | "yes")) + .unwrap_or(false) +} + +#[cfg(feature = "server")] +pub fn session_cookie(token: &str, max_age_seconds: i32, secure: bool) -> String { + let secure_flag = if secure { "; Secure" } else { "" }; + format!( + "session={token}; HttpOnly; Path=/; Max-Age={max_age_seconds}; SameSite=Lax{secure_flag}" + ) +} + #[cfg(feature = "server")] pub fn parse_session_token(cookie_header: &str) -> Option<&str> { cookie_header.split(';').map(|s| s.trim()).find_map(|pair| { @@ -132,4 +148,25 @@ mod tests { let expected = sha2::Sha256::digest(b"hello"); assert_eq!(hash, hex::encode(expected)); } + + #[test] + fn session_cookie_without_secure() { + let cookie = session_cookie("abc", 3600, false); + assert!(cookie.contains("session=abc")); + assert!(cookie.contains("HttpOnly")); + assert!(cookie.contains("SameSite=Lax")); + assert!(!cookie.contains("Secure")); + } + + #[test] + fn session_cookie_with_secure() { + let cookie = session_cookie("abc", 3600, true); + assert!(cookie.contains("Secure")); + } + + #[test] + fn session_cookie_logout_has_zero_max_age() { + let cookie = session_cookie("", 0, false); + assert!(cookie.contains("Max-Age=0")); + } }