use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SessionConfig { pub cookie_name: String, pub max_age: i64, #[serde(default = "default_anonymous_max_age")] pub anonymous_max_age: i64, pub path: Option, pub domain: Option, pub secure: bool, pub same_site: SameSite, #[serde(default)] pub trust_proxy: bool, #[serde(default = "default_recheck_secs")] pub recheck_secs: u64, } fn default_anonymous_max_age() -> i64 { 300 } fn default_recheck_secs() -> u64 { 60 } impl Default for SessionConfig { fn default() -> Self { Self { cookie_name: "session".to_owned(), max_age: 2_592_000, anonymous_max_age: default_anonymous_max_age(), path: Some("/".to_owned()), domain: None, secure: false, same_site: SameSite::default(), trust_proxy: false, recheck_secs: default_recheck_secs(), } } } /// `secure=false` 只允许开发环境:回环监听,或显式 `KENOAUTH_ALLOW_INSECURE_COOKIE=1`。 pub fn ensure_secure_cookie( secure: bool, http_addr: std::net::SocketAddr, allow_insecure_env: bool, ) -> Result<(), String> { if secure || allow_insecure_env || http_addr.ip().is_loopback() { return Ok(()); } Err(format!( "session.secure=false is only allowed in development; {http_addr} is not a loopback \ address. Set session.secure=true, or set KENOAUTH_ALLOW_INSECURE_COOKIE=1 to confirm" )) } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum SameSite { Strict, /// 浏览器与 RFC626bis 的实际默认值;显式配置 None 时需自行保证 Secure。 #[default] Lax, None, } impl crate::loader::Section for SessionConfig { const KEY: &'static str = "session"; } #[cfg(test)] mod tests { use super::*; fn addr(ip: [u8; 4]) -> std::net::SocketAddr { std::net::SocketAddr::from((ip, 18081)) } #[test] fn insecure_cookie_is_only_allowed_in_development() { assert!(ensure_secure_cookie(true, addr([0, 0, 0, 0]), false).is_ok()); assert!(ensure_secure_cookie(false, addr([127, 0, 0, 1]), false).is_ok()); assert!(ensure_secure_cookie(false, addr([192, 168, 1, 10]), true).is_ok()); let error = ensure_secure_cookie(false, addr([0, 0, 0, 0]), false).unwrap_err(); assert!(error.contains("session.secure")); } }