fix(security): make session insert synchronous and default cookie Secure flag to on

- Await session DB insert before setting cookie, preventing orphaned sessions
- Invert cookie Secure default: now on by default, opt out via
  BREWLOG_INSECURE_COOKIES=true for local HTTP development
This commit is contained in:
Jon Seager 2026-02-06 17:52:01 +00:00
parent 85946ba9a4
commit 533a0a067f
No known key found for this signature in database

View file

@ -178,7 +178,7 @@ pub(crate) async fn register_finish(
info!(user_id = %user_id, "passkey registered successfully"); info!(user_id = %user_id, "passkey registered successfully");
// Create session for the new user // Create session for the new user
create_session(&state, &cookies, user_id); create_session(&state, &cookies, user_id).await;
Ok(Json(AuthFinishResponse { redirect: None })) Ok(Json(AuthFinishResponse { redirect: None }))
} }
@ -368,7 +368,7 @@ pub(crate) async fn auth_finish(
} }
// Normal web login: create session // Normal web login: create session
create_session(&state, &cookies, user_id); create_session(&state, &cookies, user_id).await;
info!(user_id = %user_id, "user authenticated via passkey"); info!(user_id = %user_id, "user authenticated via passkey");
@ -482,7 +482,7 @@ pub(crate) async fn passkey_add_finish(
// --- Helpers --- // --- Helpers ---
fn create_session(state: &AppState, cookies: &Cookies, user_id: crate::domain::ids::UserId) { async fn create_session(state: &AppState, cookies: &Cookies, user_id: crate::domain::ids::UserId) {
let session_token = generate_session_token(); let session_token = generate_session_token();
let session_token_hash = hash_token(&session_token); let session_token_hash = hash_token(&session_token);
@ -493,20 +493,18 @@ fn create_session(state: &AppState, cookies: &Cookies, user_id: crate::domain::i
Utc::now() + Duration::days(30), Utc::now() + Duration::days(30),
); );
// Store the session in a fire-and-forget spawn, set the cookie optimistically. if let Err(err) = state.session_repo.insert(new_session).await {
let session_repo = state.session_repo.clone(); error!(error = %err, "failed to create session");
tokio::spawn(async move { return;
if let Err(err) = session_repo.insert(new_session).await { }
error!(error = %err, "failed to create session");
}
});
let mut cookie = Cookie::new(SESSION_COOKIE_NAME, session_token); let mut cookie = Cookie::new(SESSION_COOKIE_NAME, session_token);
cookie.set_path("/"); cookie.set_path("/");
cookie.set_http_only(true); cookie.set_http_only(true);
cookie.set_same_site(tower_cookies::cookie::SameSite::Lax); cookie.set_same_site(tower_cookies::cookie::SameSite::Lax);
if std::env::var("BREWLOG_SECURE_COOKIES").unwrap_or_default() == "true" { let insecure = std::env::var("BREWLOG_INSECURE_COOKIES").unwrap_or_default() == "true";
if !insecure {
cookie.set_secure(true); cookie.set_secure(true);
} }