diff --git a/src/server/auth.rs b/src/server/auth.rs index 9c366a8..1e0995d 100644 --- a/src/server/auth.rs +++ b/src/server/auth.rs @@ -5,11 +5,14 @@ use axum::{ middleware::Next, response::{IntoResponse, Response}, }; +use tower_cookies::Cookies; use crate::domain::users::User; use crate::infrastructure::auth::hash_token; use crate::server::server::AppState; +const SESSION_COOKIE_NAME: &str = "brewlog_session"; + /// Extension type to carry authenticated user through request handlers #[derive(Clone)] pub struct AuthenticatedUser(pub User); @@ -27,7 +30,14 @@ impl FromRequestParts for AuthenticatedUser { return Ok(user.clone()); } - // Otherwise, extract from Authorization header directly + // Try to authenticate via session cookie first + if let Ok(cookies) = Cookies::from_request_parts(parts, state).await { + if let Some(user) = authenticate_via_session(state, &cookies).await { + return Ok(AuthenticatedUser(user)); + } + } + + // Fall back to Bearer token authentication let auth_header = parts .headers .get(header::AUTHORIZATION) @@ -73,6 +83,27 @@ impl FromRequestParts for AuthenticatedUser { } } +/// Authenticate via session cookie +async fn authenticate_via_session(state: &AppState, cookies: &Cookies) -> Option { + let cookie = cookies.get(SESSION_COOKIE_NAME)?; + let session_token = cookie.value(); + let session_token_hash = hash_token(session_token); + + // Check if session exists and is valid + let session = state + .session_repo + .get_by_token_hash(&session_token_hash) + .await + .ok()?; + + if session.is_expired() { + return None; + } + + // Get the user + state.user_repo.get(session.user_id).await.ok() +} + /// Middleware that optionally extracts authentication from the request /// Sets AuthenticatedUser extension if valid token is found pub async fn optional_auth_middleware( diff --git a/src/server/routes/auth.rs b/src/server/routes/auth.rs index 75eba66..235e868 100644 --- a/src/server/routes/auth.rs +++ b/src/server/routes/auth.rs @@ -92,8 +92,13 @@ pub(crate) async fn login_submit( let mut cookie = Cookie::new(SESSION_COOKIE_NAME, session_token); cookie.set_path("/"); cookie.set_http_only(true); - cookie.set_same_site(tower_cookies::cookie::SameSite::Lax); - // In production, set secure flag: cookie.set_secure(true); + cookie.set_same_site(tower_cookies::cookie::SameSite::Strict); + + // Enable secure flag if BREWLOG_SECURE_COOKIES is set to "true" + // This should be enabled in production when serving over HTTPS + if std::env::var("BREWLOG_SECURE_COOKIES").unwrap_or_default() == "true" { + cookie.set_secure(true); + } cookies.add(cookie);