feat(server): auto-enable insecure cookies for localhost defaults

Default BREWLOG_RP_ID to "localhost" and BREWLOG_RP_ORIGIN to
"http://localhost:3000". When these localhost defaults are active,
insecure cookies are enabled automatically so local dev works without
setting BREWLOG_INSECURE_COOKIES. A warning is logged at startup when
insecure cookies are active.

The insecure_cookies flag is now threaded through AppState instead of
reading the env var at cookie-set time.
This commit is contained in:
Jon Seager 2026-02-09 17:23:09 +00:00
parent 43b5ccb34a
commit 000f20eab7
No known key found for this signature in database
7 changed files with 29 additions and 18 deletions

View file

@ -487,8 +487,7 @@ async fn create_session(state: &AppState, cookies: &Cookies, user_id: crate::dom
cookie.set_http_only(true);
cookie.set_same_site(tower_cookies::cookie::SameSite::Lax);
let insecure = std::env::var("BREWLOG_INSECURE_COOKIES").unwrap_or_default() == "true";
if !insecure {
if !state.insecure_cookies {
cookie.set_secure(true);
}

View file

@ -22,6 +22,7 @@ pub struct ServerConfig {
pub database_url: String,
pub rp_id: String,
pub rp_origin: String,
pub insecure_cookies: bool,
pub openrouter_api_key: String,
pub openrouter_model: String,
pub foursquare_api_key: String,
@ -48,6 +49,7 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
&database,
AppStateConfig {
webauthn,
insecure_cookies: config.insecure_cookies,
foursquare_url: crate::infrastructure::foursquare::FOURSQUARE_SEARCH_URL.to_string(),
foursquare_api_key: config.foursquare_api_key,
openrouter_url: crate::infrastructure::ai::OPENROUTER_URL.to_string(),

View file

@ -36,6 +36,7 @@ use crate::infrastructure::webauthn::ChallengeStore;
/// automatically from the database pool.
pub struct AppStateConfig {
pub webauthn: Arc<Webauthn>,
pub insecure_cookies: bool,
pub foursquare_url: String,
pub foursquare_api_key: String,
pub openrouter_url: String,
@ -77,6 +78,7 @@ pub struct AppState {
pub gear_service: GearService,
pub cafe_service: CafeService,
pub cup_service: CupService,
pub insecure_cookies: bool,
pub stats_invalidator: StatsInvalidator,
}
@ -164,6 +166,7 @@ impl AppState {
gear_service,
cafe_service,
cup_service,
insecure_cookies: config.insecure_cookies,
stats_invalidator: config.stats_invalidator,
}
}

View file

@ -70,19 +70,16 @@ async fn main() -> Result<()> {
}
async fn run_server(command: ServeCommand) -> Result<()> {
let rp_id = command.rp_id.ok_or_else(|| {
anyhow::anyhow!(
"BREWLOG_RP_ID is required. Set this to the domain where the app is hosted \
(e.g. 'brewlog.example.com' or 'localhost')."
)
})?;
let rp_id = command.rp_id;
let rp_origin = command.rp_origin;
let rp_origin = command.rp_origin.ok_or_else(|| {
anyhow::anyhow!(
"BREWLOG_RP_ORIGIN is required. Set this to the full origin URL \
(e.g. 'https://brewlog.example.com' or 'http://localhost:3000')."
)
})?;
let insecure_cookies = command.insecure_cookies
|| (rp_id == "localhost" && rp_origin.starts_with("http://localhost"));
if insecure_cookies {
tracing::warn!(
"insecure cookies enabled for development/demo setup - do not use in production"
);
}
let openrouter_api_key = command.openrouter_api_key.ok_or_else(|| {
anyhow::anyhow!(
@ -105,6 +102,7 @@ async fn run_server(command: ServeCommand) -> Result<()> {
database_url: command.database_url,
rp_id,
rp_origin,
insecure_cookies,
openrouter_api_key,
openrouter_model: command.openrouter_model,
foursquare_api_key,

View file

@ -111,11 +111,18 @@ pub struct ServeCommand {
#[arg(long, env = "BREWLOG_BIND_ADDRESS", default_value = "127.0.0.1:3000")]
pub bind_address: SocketAddr,
#[arg(long, env = "BREWLOG_RP_ID")]
pub rp_id: Option<String>,
#[arg(long, env = "BREWLOG_RP_ID", default_value = "localhost")]
pub rp_id: String,
#[arg(long, env = "BREWLOG_RP_ORIGIN")]
pub rp_origin: Option<String>,
#[arg(
long,
env = "BREWLOG_RP_ORIGIN",
default_value = "http://localhost:3000"
)]
pub rp_origin: String,
#[arg(long, env = "BREWLOG_INSECURE_COOKIES")]
pub insecure_cookies: bool,
#[arg(long, env = "BREWLOG_OPENROUTER_API_KEY")]
pub openrouter_api_key: Option<String>,

View file

@ -90,6 +90,7 @@ fn ensure_server_started() -> Result<(String, String), String> {
&database,
AppStateConfig {
webauthn: test_webauthn(),
insecure_cookies: true,
foursquare_url: brewlog::infrastructure::foursquare::FOURSQUARE_SEARCH_URL
.to_string(),
foursquare_api_key: String::new(),

View file

@ -76,6 +76,7 @@ fn test_state_config() -> AppStateConfig {
let (tx, _rx) = tokio::sync::mpsc::channel(1);
AppStateConfig {
webauthn: test_webauthn(),
insecure_cookies: true,
foursquare_url: brewlog::infrastructure::foursquare::FOURSQUARE_SEARCH_URL.to_string(),
foursquare_api_key: String::new(),
openrouter_url: brewlog::infrastructure::ai::OPENROUTER_URL.to_string(),